<!DOCTYPE html><html lang="zh-CN" data-theme="light"><head><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1.0,viewport-fit=cover"><title>数据结构与算法1 | 纸飞机的旅行</title><meta name="author" content="GISer"><meta name="copyright" content="GISer"><meta name="format-detection" content="telephone=no"><meta name="theme-color" content="#ffffff"><meta name="description" content="一. 初识算法1.1 什么是算法？定义 在数学和计算机科学领域，算法是一系列有限的严谨指令，通常用于解决一类特定问题或执行计算  In mathematics and computer science, an algorithm (&#x2F;ˈælɡərɪðəm&#x2F;) is a finite sequence of rigorous instructions, typically use">
<meta property="og:type" content="article">
<meta property="og:title" content="数据结构与算法1">
<meta property="og:url" content="https://jhcgnb.cn/posts/239d0cb7/index.html">
<meta property="og:site_name" content="纸飞机的旅行">
<meta property="og:description" content="一. 初识算法1.1 什么是算法？定义 在数学和计算机科学领域，算法是一系列有限的严谨指令，通常用于解决一类特定问题或执行计算  In mathematics and computer science, an algorithm (&#x2F;ˈælɡərɪðəm&#x2F;) is a finite sequence of rigorous instructions, typically use">
<meta property="og:locale" content="zh_CN">
<meta property="og:image" content="http://rx6zk4j2b.hn-bkt.clouddn.com/blogs/image-20230110084245095.png">
<meta property="article:published_time" content="2023-01-08T14:02:28.000Z">
<meta property="article:modified_time" content="2023-07-18T14:18:17.422Z">
<meta property="article:author" content="GISer">
<meta property="article:tag" content="数据结构与算法1">
<meta name="twitter:card" content="summary">
<meta name="twitter:image" content="http://rx6zk4j2b.hn-bkt.clouddn.com/blogs/image-20230110084245095.png"><link rel="shortcut icon" href="/img/adaver.png"><link rel="canonical" href="https://jhcgnb.cn/posts/239d0cb7/index.html"><link rel="preconnect" href="//cdn.jsdelivr.net"/><link rel="preconnect" href="//www.google-analytics.com" crossorigin=""/><link rel="preconnect" href="//hm.baidu.com"/><link rel="preconnect" href="//busuanzi.ibruce.info"/><meta name="baidu-site-verification" content="codeva-Iy1bfAu5vL"/><meta name="google-site-verification" content="wN-6FUBZ4DJeo_lSZyCGmnnTBKG3n0H20plO_qsfTPM"/><link rel="stylesheet" href="/css/index.css"><link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fortawesome/fontawesome-free/css/all.min.css" media="print" onload="this.media='all'"><link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fancyapps/ui/dist/fancybox/fancybox.min.css" media="print" onload="this.media='all'"><script>var _hmt = _hmt || [];
(function() {
  var hm = document.createElement("script");
  hm.src = "https://hm.baidu.com/hm.js?c6e8056e906ef79846cd727f753be936";
  var s = document.getElementsByTagName("script")[0]; 
  s.parentNode.insertBefore(hm, s);
})();
</script><script async="async" src="https://www.googletagmanager.com/gtag/js?id=5757554821"></script><script>window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', '5757554821');
</script><script>const GLOBAL_CONFIG = {
  root: '/',
  algolia: undefined,
  localSearch: {"path":"/search.xml","preload":false,"top_n_per_article":1,"unescape":false,"languages":{"hits_empty":"找不到您查询的内容：${query}","hits_stats":"共找到 ${hits} 篇文章"}},
  translate: undefined,
  noticeOutdate: undefined,
  highlight: {"plugin":"highlighjs","highlightCopy":true,"highlightLang":true,"highlightHeightLimit":false},
  copy: {
    success: '复制成功',
    error: '复制错误',
    noSupport: '浏览器不支持'
  },
  relativeDate: {
    homepage: false,
    post: false
  },
  runtime: '',
  dateSuffix: {
    just: '刚刚',
    min: '分钟前',
    hour: '小时前',
    day: '天前',
    month: '个月前'
  },
  copyright: undefined,
  lightbox: 'fancybox',
  Snackbar: undefined,
  source: {
    justifiedGallery: {
      js: 'https://cdn.jsdelivr.net/npm/flickr-justified-gallery/dist/fjGallery.min.js',
      css: 'https://cdn.jsdelivr.net/npm/flickr-justified-gallery/dist/fjGallery.min.css'
    }
  },
  isPhotoFigcaption: false,
  islazyload: true,
  isAnchor: false,
  percent: {
    toc: true,
    rightside: false,
  },
  autoDarkmode: false
}</script><script id="config-diff">var GLOBAL_CONFIG_SITE = {
  title: '数据结构与算法1',
  isPost: true,
  isHome: false,
  isHighlightShrink: false,
  isToc: true,
  postUpdate: '2023-07-18 22:18:17'
}</script><noscript><style type="text/css">
  #nav {
    opacity: 1
  }
  .justified-gallery img {
    opacity: 1
  }

  #recent-posts time,
  #post-meta time {
    display: inline !important
  }
</style></noscript><script>(win=>{
    win.saveToLocal = {
      set: function setWithExpiry(key, value, ttl) {
        if (ttl === 0) return
        const now = new Date()
        const expiryDay = ttl * 86400000
        const item = {
          value: value,
          expiry: now.getTime() + expiryDay,
        }
        localStorage.setItem(key, JSON.stringify(item))
      },

      get: function getWithExpiry(key) {
        const itemStr = localStorage.getItem(key)

        if (!itemStr) {
          return undefined
        }
        const item = JSON.parse(itemStr)
        const now = new Date()

        if (now.getTime() > item.expiry) {
          localStorage.removeItem(key)
          return undefined
        }
        return item.value
      }
    }
  
    win.getScript = url => new Promise((resolve, reject) => {
      const script = document.createElement('script')
      script.src = url
      script.async = true
      script.onerror = reject
      script.onload = script.onreadystatechange = function() {
        const loadState = this.readyState
        if (loadState && loadState !== 'loaded' && loadState !== 'complete') return
        script.onload = script.onreadystatechange = null
        resolve()
      }
      document.head.appendChild(script)
    })
  
    win.getCSS = (url,id = false) => new Promise((resolve, reject) => {
      const link = document.createElement('link')
      link.rel = 'stylesheet'
      link.href = url
      if (id) link.id = id
      link.onerror = reject
      link.onload = link.onreadystatechange = function() {
        const loadState = this.readyState
        if (loadState && loadState !== 'loaded' && loadState !== 'complete') return
        link.onload = link.onreadystatechange = null
        resolve()
      }
      document.head.appendChild(link)
    })
  
      win.activateDarkMode = function () {
        document.documentElement.setAttribute('data-theme', 'dark')
        if (document.querySelector('meta[name="theme-color"]') !== null) {
          document.querySelector('meta[name="theme-color"]').setAttribute('content', '#0d0d0d')
        }
      }
      win.activateLightMode = function () {
        document.documentElement.setAttribute('data-theme', 'light')
        if (document.querySelector('meta[name="theme-color"]') !== null) {
          document.querySelector('meta[name="theme-color"]').setAttribute('content', '#ffffff')
        }
      }
      const t = saveToLocal.get('theme')
    
          if (t === 'dark') activateDarkMode()
          else if (t === 'light') activateLightMode()
        
      const asideStatus = saveToLocal.get('aside-status')
      if (asideStatus !== undefined) {
        if (asideStatus === 'hide') {
          document.documentElement.classList.add('hide-aside')
        } else {
          document.documentElement.classList.remove('hide-aside')
        }
      }
    
    const detectApple = () => {
      if(/iPad|iPhone|iPod|Macintosh/.test(navigator.userAgent)){
        document.documentElement.classList.add('apple')
      }
    }
    detectApple()
    })(window)</script><meta name="generator" content="Hexo 6.3.0"><link rel="alternate" href="/rss2.xml" title="纸飞机的旅行" type="application/rss+xml">
</head><body><div id="web_bg"></div><div id="sidebar"><div id="menu-mask"></div><div id="sidebar-menus"><div class="avatar-img is-center"><img src= "" data-lazy-src="/img/adaver.png" onerror="onerror=null;src='/img/friend_404.gif'" alt="avatar"/></div><div class="sidebar-site-data site-data is-center"><a href="/archives/"><div class="headline">文章</div><div class="length-num">20</div></a><a href="/tags/"><div class="headline">标签</div><div class="length-num">7</div></a><a href="/categories/"><div class="headline">分类</div><div class="length-num">3</div></a></div><hr class="custom-hr"/><div class="menus_items"><div class="menus_item"><a class="site-page" href="/"><i class="fa-fw fas fa-home"></i><span> 主页</span></a></div><div class="menus_item"><a class="site-page group" href="javascript:void(0);"><i class="fa-fw fa fa-graduation-cap"></i><span> 博文</span><i class="fas fa-chevron-down"></i></a><ul class="menus_item_child"><li><a class="site-page child" href="/categories/"><i class="fa-fw fa fa-archive"></i><span> 分类</span></a></li><li><a class="site-page child" href="/tags/"><i class="fa-fw fa fa-tags"></i><span> 标签</span></a></li><li><a class="site-page child" href="/archives/"><i class="fa-fw fa fa-folder-open"></i><span> 归档</span></a></li></ul></div><div class="menus_item"><a class="site-page group" href="javascript:void(0);"><i class="fa-fw fas fa-list"></i><span> 生活</span><i class="fas fa-chevron-down"></i></a><ul class="menus_item_child"><li><a class="site-page child" href="/shuoshuo/"><i class="fa-fw fa fa-comments-o"></i><span> 分享</span></a></li><li><a class="site-page child" href="/photos/"><i class="fa-fw fa fa-camera-retro"></i><span> 相册</span></a></li><li><a class="site-page child" href="/music/"><i class="fa-fw fa fa-music"></i><span> 音乐</span></a></li><li><a class="site-page child" href="/movies/"><i class="fa-fw fas fa-video"></i><span> 影视</span></a></li></ul></div><div class="menus_item"><a class="site-page" href="/links/"><i class="fa-fw fa fa-link"></i><span> 友链</span></a></div><div class="menus_item"><a class="site-page" href="/comment/"><i class="fa-fw fa fa-paper-plane"></i><span> 留言板</span></a></div><div class="menus_item"><a class="site-page" href="/about/"><i class="fa-fw fas fa-heart"></i><span> 关于笔者</span></a></div></div></div></div><div class="post" id="body-wrap"><header class="post-bg" id="page-header" style="background-image: url('http://rx6zk4j2b.hn-bkt.clouddn.com/blogs/image-20230110084245095.png')"><nav id="nav"><span id="blog-info"><a href="/" title="纸飞机的旅行"><span class="site-name">纸飞机的旅行</span></a></span><div id="menus"><div id="search-button"><a class="site-page social-icon search" href="javascript:void(0);"><i class="fas fa-search fa-fw"></i><span> 搜索</span></a></div><div class="menus_items"><div class="menus_item"><a class="site-page" href="/"><i class="fa-fw fas fa-home"></i><span> 主页</span></a></div><div class="menus_item"><a class="site-page group" href="javascript:void(0);"><i class="fa-fw fa fa-graduation-cap"></i><span> 博文</span><i class="fas fa-chevron-down"></i></a><ul class="menus_item_child"><li><a class="site-page child" href="/categories/"><i class="fa-fw fa fa-archive"></i><span> 分类</span></a></li><li><a class="site-page child" href="/tags/"><i class="fa-fw fa fa-tags"></i><span> 标签</span></a></li><li><a class="site-page child" href="/archives/"><i class="fa-fw fa fa-folder-open"></i><span> 归档</span></a></li></ul></div><div class="menus_item"><a class="site-page group" href="javascript:void(0);"><i class="fa-fw fas fa-list"></i><span> 生活</span><i class="fas fa-chevron-down"></i></a><ul class="menus_item_child"><li><a class="site-page child" href="/shuoshuo/"><i class="fa-fw fa fa-comments-o"></i><span> 分享</span></a></li><li><a class="site-page child" href="/photos/"><i class="fa-fw fa fa-camera-retro"></i><span> 相册</span></a></li><li><a class="site-page child" href="/music/"><i class="fa-fw fa fa-music"></i><span> 音乐</span></a></li><li><a class="site-page child" href="/movies/"><i class="fa-fw fas fa-video"></i><span> 影视</span></a></li></ul></div><div class="menus_item"><a class="site-page" href="/links/"><i class="fa-fw fa fa-link"></i><span> 友链</span></a></div><div class="menus_item"><a class="site-page" href="/comment/"><i class="fa-fw fa fa-paper-plane"></i><span> 留言板</span></a></div><div class="menus_item"><a class="site-page" href="/about/"><i class="fa-fw fas fa-heart"></i><span> 关于笔者</span></a></div></div><div id="toggle-menu"><a class="site-page" href="javascript:void(0);"><i class="fas fa-bars fa-fw"></i></a></div></div></nav><div id="post-info"><h1 class="post-title">数据结构与算法1</h1><div id="post-meta"><div class="meta-firstline"><span class="post-meta-date"><i class="far fa-calendar-alt fa-fw post-meta-icon"></i><span class="post-meta-label">发表于</span><time class="post-meta-date-created" datetime="2023-01-08T14:02:28.000Z" title="发表于 2023-01-08 22:02:28">2023-01-08</time><span class="post-meta-separator">|</span><i class="fas fa-history fa-fw post-meta-icon"></i><span class="post-meta-label">更新于</span><time class="post-meta-date-updated" datetime="2023-07-18T14:18:17.422Z" title="更新于 2023-07-18 22:18:17">2023-07-18</time></span><span class="post-meta-categories"><span class="post-meta-separator">|</span><i class="fas fa-inbox fa-fw post-meta-icon"></i><a class="post-meta-categories" href="/categories/%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84%E4%B8%8E%E7%AE%97%E6%B3%95/">数据结构与算法</a></span></div><div class="meta-secondline"><span class="post-meta-separator">|</span><span class="post-meta-wordcount"><i class="far fa-file-word fa-fw post-meta-icon"></i><span class="post-meta-label">字数总计:</span><span class="word-count">38.7k</span><span class="post-meta-separator">|</span><i class="far fa-clock fa-fw post-meta-icon"></i><span class="post-meta-label">阅读时长:</span><span>178分钟</span></span><span class="post-meta-separator">|</span><span class="post-meta-pv-cv" id="" data-flag-title="数据结构与算法1"><i class="far fa-eye fa-fw post-meta-icon"></i><span class="post-meta-label">阅读量:</span><span id="busuanzi_value_page_pv"><i class="fa-solid fa-spinner fa-spin"></i></span></span></div></div></div></header><main class="layout" id="content-inner"><div id="post"><article class="post-content" id="article-container"><h1 id="一-初识算法"><a href="#一-初识算法" class="headerlink" title="一. 初识算法"></a>一. 初识算法</h1><h2 id="1-1-什么是算法？"><a href="#1-1-什么是算法？" class="headerlink" title="1.1 什么是算法？"></a>1.1 什么是算法？</h2><p><strong>定义</strong></p>
<p>在数学和计算机科学领域，算法是一系列有限的严谨指令，通常用于解决一类特定问题或执行计算</p>
<blockquote>
<p>In mathematics and computer science, an <strong>algorithm</strong> (&#x2F;ˈælɡərɪðəm&#x2F;) is a finite sequence of rigorous instructions, typically used to solve a class of specific problems or to perform a computation.[^1]</p>
</blockquote>
<p><strong>Introduction to Algorithm[^2]</strong></p>
<p>不正式的说，算法就是任何定义优良的计算过程：接收一些值作为输入，在有限的时间内，产生一些值作为输出。</p>
<blockquote>
<p>Informally, an algorithm is any well-defined computational procedure that takes some value, or set of values, as input and produces some value, or set of values, as output in a finite amount of time.</p>
</blockquote>
<h2 id="1-2-什么是数据结构？"><a href="#1-2-什么是数据结构？" class="headerlink" title="1.2 什么是数据结构？"></a>1.2 什么是数据结构？</h2><p><strong>定义</strong></p>
<p>在计算机科学领域，数据结构是一种数据组织、管理和存储格式，通常被选择用来高效访问数据</p>
<blockquote>
<p>In computer science, a <strong>data structure</strong> is a data organization, management, and storage format that is usually chosen for efficient access to data</p>
</blockquote>
<p><strong>Introduction to Algorithm[^2]</strong></p>
<p>数据结构是一种存储和组织数据的方式，旨在便于访问和修改</p>
<blockquote>
<p>A data structure is a way to store and organize data in order to facilitate access and modifications</p>
</blockquote>
<p>接下来我们通过对一个非常著名的二分查找算法的讲解来认识一下算法</p>
<h2 id="1-3-二分查找-3"><a href="#1-3-二分查找-3" class="headerlink" title="1.3 二分查找 [^3]"></a>1.3 二分查找 [^3]</h2><p>二分查找算法也称折半查找，是一种非常高效的工作于有序数组的查找算法。后续的课程中还会学习更多的查找算法，但在此之前，不妨用它作为入门。</p>
<h3 id="二分查找基础版"><a href="#二分查找基础版" class="headerlink" title="二分查找基础版"></a>二分查找基础版</h3><p>需求：在<strong>有序</strong>数组 $A$ 内，查找值 $target$</p>
<ul>
<li>如果找到返回索引</li>
<li>如果找不到返回 $-1$</li>
</ul>
<table>
<thead>
<tr>
<th>算法描述</th>
<th></th>
</tr>
</thead>
<tbody><tr>
<td>前提</td>
<td>给定一个内含 $n$ 个元素的有序数组 $A$，满足 $A_{0}\leq A_{1}\leq A_{2}\leq \cdots \leq A_{n-1}$，一个待查值 $target$</td>
</tr>
<tr>
<td>1</td>
<td>设置 $i&#x3D;0$，$j&#x3D;n-1$</td>
</tr>
<tr>
<td>2</td>
<td>如果 $i \gt j$，结束查找，没找到</td>
</tr>
<tr>
<td>3</td>
<td>设置 $m &#x3D; floor(\frac {i+j}{2})$ ，$m$ 为中间索引，$floor$ 是向下取整（$\leq \frac {i+j}{2}$ 的最小整数）</td>
</tr>
<tr>
<td>4</td>
<td>如果 $target &lt; A_{m}$ 设置 $j &#x3D; m - 1$，跳到第2步</td>
</tr>
<tr>
<td>5</td>
<td>如果 $A_{m} &lt; target$ 设置 $i &#x3D; m + 1$，跳到第2步</td>
</tr>
<tr>
<td>6</td>
<td>如果 $A_{m} &#x3D; target$，结束查找，找到了</td>
</tr>
</tbody></table>
<p>java 实现</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">static</span> <span class="type">int</span> <span class="title function_">binarySearch</span><span class="params">(<span class="type">int</span>[] a, <span class="type">int</span> target)</span> &#123;</span><br><span class="line">    <span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> <span class="number">0</span>, j = a.length - <span class="number">1</span>;</span><br><span class="line">    <span class="keyword">while</span> (i &lt;= j) &#123;</span><br><span class="line">        <span class="type">int</span> <span class="variable">m</span> <span class="operator">=</span> (i + j) &gt;&gt;&gt; <span class="number">1</span>;</span><br><span class="line">        <span class="keyword">if</span> (target &lt; a[m]) &#123;			<span class="comment">// 在左边</span></span><br><span class="line">            j = m - <span class="number">1</span>;</span><br><span class="line">        &#125; <span class="keyword">else</span> <span class="keyword">if</span> (a[m] &lt; target) &#123;		<span class="comment">// 在右边</span></span><br><span class="line">            i = m + <span class="number">1</span>;</span><br><span class="line">        &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">            <span class="keyword">return</span> m;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> -<span class="number">1</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<ul>
<li>$i,j$ 对应着搜索区间 $[0,a.length-1]$（注意是闭合的区间），$i&lt;&#x3D;j$ 意味着搜索区间内还有未比较的元素，$i,j$ 指向的元素也可能是比较的目标<ul>
<li>思考：如果不加 $i&#x3D;&#x3D;j$ 行不行？</li>
<li>回答：不行，因为这意味着 $i,j$ 指向的元素会漏过比较</li>
</ul>
</li>
<li>$m$ 对应着中间位置，中间位置左边和右边的元素可能不相等（差一个），不会影响结果</li>
<li>如果某次未找到，那么缩小后的区间内不包含 $m$</li>
</ul>
<h3 id="二分查找改变版"><a href="#二分查找改变版" class="headerlink" title="二分查找改变版"></a>二分查找改变版</h3><p>另一种写法</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">static</span> <span class="type">int</span> <span class="title function_">binarySearch</span><span class="params">(<span class="type">int</span>[] a, <span class="type">int</span> target)</span> &#123;</span><br><span class="line">    <span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> <span class="number">0</span>, j = a.length;</span><br><span class="line">    <span class="keyword">while</span> (i &lt; j) &#123;</span><br><span class="line">        <span class="type">int</span> <span class="variable">m</span> <span class="operator">=</span> (i + j) &gt;&gt;&gt; <span class="number">1</span>;</span><br><span class="line">        <span class="keyword">if</span> (target &lt; a[m]) &#123;			<span class="comment">// 在左边</span></span><br><span class="line">            j = m;</span><br><span class="line">        &#125; <span class="keyword">else</span> <span class="keyword">if</span> (a[m] &lt; target) &#123;		<span class="comment">// 在右边</span></span><br><span class="line">            i = m + <span class="number">1</span>;</span><br><span class="line">        &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">            <span class="keyword">return</span> m;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> -<span class="number">1</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<ul>
<li>$i,j$ 对应着搜索区间 $[0,a.length)$（注意是左闭右开的区间），$i&lt;j$ 意味着搜索区间内还有未比较的元素，$j$ 指向的<strong>一定不是</strong>查找目标<ul>
<li>思考：为啥这次不加 $i&#x3D;&#x3D;j$ 的条件了？</li>
<li>回答：这回 $j$ 指向的不是查找目标，如果还加 $i&#x3D;&#x3D;j$ 条件，就意味着 $j$ 指向的还会再次比较，找不到时，会死循环</li>
</ul>
</li>
<li>如果某次要缩小右边界，那么 $j&#x3D;m$，因为此时的 $m$ 已经<strong>不是</strong>查找目标了</li>
</ul>
<h3 id="衡量算法好坏"><a href="#衡量算法好坏" class="headerlink" title="衡量算法好坏"></a>衡量算法好坏</h3><p><strong>时间复杂度</strong></p>
<p>下面的查找算法也能得出与之前二分查找一样的结果，那你能说出它差在哪里吗？</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">static</span> <span class="type">int</span> <span class="title function_">search</span><span class="params">(<span class="type">int</span>[] a, <span class="type">int</span> k)</span> &#123;</span><br><span class="line">    <span class="keyword">for</span> (</span><br><span class="line">        <span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> <span class="number">0</span>;</span><br><span class="line">        i &lt; a.length;</span><br><span class="line">        i++</span><br><span class="line">    ) &#123;</span><br><span class="line">        <span class="keyword">if</span> (a[i] == k) &#123;</span><br><span class="line">            <span class="keyword">return</span> i;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> -<span class="number">1</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>考虑最坏情况下（没找到）例如 <code>[1,2,3,4]</code> 查找 5</p>
<ul>
<li><code>int i = 0</code> 只执行一次</li>
<li><code>i &lt; a.length</code> 受数组元素个数 $n$ 的影响，比较 $n+1$ 次</li>
<li><code>i++</code> 受数组元素个数 $n$ 的影响，自增 $n$ 次</li>
<li><code>a[i] == k</code> 受元素个数 $n$ 的影响，比较 $n$ 次</li>
<li><code>return -1</code>，执行一次</li>
</ul>
<p>粗略认为每行代码执行时间是 $t$，假设 $n&#x3D;4$ 那么</p>
<ul>
<li>总执行时间是 $(1+4+1+4+4+1)*t &#x3D; 15t$</li>
<li>可以推导出更一般地公式为，$T &#x3D; (3*n+3)t$</li>
</ul>
<p>如果套用二分查找算法，还是 <code>[1,2,3,4]</code> 查找 5</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">static</span> <span class="type">int</span> <span class="title function_">binarySearch</span><span class="params">(<span class="type">int</span>[] a, <span class="type">int</span> target)</span> &#123;</span><br><span class="line">    <span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> <span class="number">0</span>, j = a.length - <span class="number">1</span>;</span><br><span class="line">    <span class="keyword">while</span> (i &lt;= j) &#123;</span><br><span class="line">        <span class="type">int</span> <span class="variable">m</span> <span class="operator">=</span> (i + j) &gt;&gt;&gt; <span class="number">1</span>;</span><br><span class="line">        <span class="keyword">if</span> (target &lt; a[m]) &#123;			<span class="comment">// 在左边</span></span><br><span class="line">            j = m - <span class="number">1</span>;</span><br><span class="line">        &#125; <span class="keyword">else</span> <span class="keyword">if</span> (a[m] &lt; target) &#123;		<span class="comment">// 在右边</span></span><br><span class="line">            i = m + <span class="number">1</span>;</span><br><span class="line">        &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">            <span class="keyword">return</span> m;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> -<span class="number">1</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<ul>
<li><p><code>int i = 0, j = a.length - 1</code> 各执行 1 次</p>
</li>
<li><p><code>i &lt;= j</code> 比较 $floor(\log_{2}(n)+1)$ 再加 1 次</p>
</li>
<li><p><code>(i + j) &gt;&gt;&gt; 1</code> 计算 $floor(\log_{2}(n)+1)$ 次</p>
</li>
<li><p>接下来 <code>if() else if() else</code> 会执行 $3* floor(\log_{2}(n)+1)$ 次，分别为</p>
<ul>
<li>if 比较</li>
<li>else if 比较</li>
<li>else if 比较成立后的赋值语句</li>
</ul>
</li>
<li><p><code>return -1</code>，执行一次</p>
</li>
</ul>
<p>结果：</p>
<ul>
<li>总执行时间为 $(2 + (1+3) + 3 + 3 * 3 +1)*t &#x3D; 19t$</li>
<li>更一般地公式为 $(4 + 5 * floor(\log_{2}(n)+1))*t$</li>
</ul>
<blockquote>
<p><strong>注意：</strong></p>
<p>左侧未找到和右侧未找到结果不一样，这里不做分析</p>
</blockquote>
<p>两个算法比较，可以看到 $n$ 在较小的时候，二者花费的次数差不多</p>
<img src= "" data-lazy-src="http://rx6zk4j2b.hn-bkt.clouddn.com/blogs/image-20221108095747933.png" alt="image-20221108095747933" style="zoom:50%;" />

<p>但随着 $n$ 越来越大，比如说 $n&#x3D;1000$ 时，用二分查找算法（红色）也就是 $54t$，而蓝色算法则需要 $3003t$</p>
<img src= "" data-lazy-src="http://rx6zk4j2b.hn-bkt.clouddn.com/blogs/image-20221108100014451.png" alt="image-20221108100014451" style="zoom:50%;" />



<blockquote>
<p>画图采用的是 <a target="_blank" rel="noopener" href="https://www.desmos.com/calculator?lang=zh-CN">Desmos | 图形计算器</a></p>
</blockquote>
<p>计算机科学中，<strong>时间复杂度</strong>是用来衡量：一个算法的执行，随数据规模增大，而增长的时间成本</p>
<ul>
<li>不依赖于环境因素</li>
</ul>
<p>如何表示时间复杂度呢？</p>
<ul>
<li><p>假设算法要处理的数据规模是 $n$，代码总的执行行数用函数 $f(n)$ 来表示，例如：</p>
<ul>
<li>线性查找算法的函数 $f(n) &#x3D; 3*n + 3$</li>
<li>二分查找算法的函数 $f(n) &#x3D; (floor(log_2(n)) + 1) * 5 + 4$</li>
</ul>
</li>
<li><p>为了对 $f(n)$ 进行化简，应当抓住主要矛盾，找到一个变化趋势与之相近的表示法</p>
</li>
</ul>
<p><strong>大 $O$ 表示法[^4]</strong></p>
<p><img src= "" data-lazy-src="http://rx6zk4j2b.hn-bkt.clouddn.com/blogs/image-20221108103846566.png" alt="image-20221108103846566"></p>
<p>其中</p>
<ul>
<li>$c, c_1, c_2$ 都为一个常数</li>
<li>$f(n)$ 是实际执行代码行数与 n 的函数</li>
<li>$g(n)$ 是经过化简，变化趋势与 $f(n)$ 一致的 n 的函数</li>
</ul>
<p><strong>渐进上界</strong></p>
<p>渐进上界（asymptotic upper bound）：从某个常数 $n_0$开始，$c*g(n)$ 总是位于 $f(n)$ 上方，那么记作 $O(g(n))$</p>
<ul>
<li>代表算法执行的最差情况</li>
</ul>
<p>例1</p>
<ul>
<li>$f(n) &#x3D; 3*n+3$ </li>
<li>$g(n) &#x3D; n$</li>
<li>取 $c&#x3D;4$，在$n_0&#x3D;3$ 之后，$g(n)$ 可以作为 $f(n)$ 的渐进上界，因此表示法写作 $O(n)$</li>
</ul>
<p>例2</p>
<ul>
<li>$f(n) &#x3D; 5*floor(log_2(n)) + 9$</li>
<li>$g(n) &#x3D; log_2(n)$</li>
<li>$O(log_2(n))$</li>
</ul>
<p>已知 $f(n)$ 来说，求 $g(n)$</p>
<ul>
<li>表达式中相乘的常量，可以省略，如<ul>
<li>$f(n) &#x3D; 100*n^2$ 中的 $100$</li>
</ul>
</li>
<li>多项式中数量规模更小（低次项）的表达式，如<ul>
<li>$f(n)&#x3D;n^2+n$ 中的 $n$</li>
<li>$f(n) &#x3D; n^3 + n^2$ 中的 $n^2$</li>
</ul>
</li>
<li>不同底数的对数，渐进上界可以用一个对数函数 $\log n$ 表示<ul>
<li>例如：$log_2(n)$ 可以替换为 $log_{10}(n)$，因为 $log_2(n) &#x3D; \frac{log_{10}(n)}{log_{10}(2)}$，相乘的常量 $\frac{1}{log_{10}(2)}$ 可以省略</li>
</ul>
</li>
<li>类似的，对数的常数次幂可省略<ul>
<li>如：$log(n^c) &#x3D; c * log(n)$</li>
</ul>
</li>
</ul>
<p><strong>常见大 $O$ 表示法</strong></p>
<p><img src= "" data-lazy-src="http://rx6zk4j2b.hn-bkt.clouddn.com/blogs/image-20221108114915524.png" alt="image-20221108114915524"></p>
<p> 按时间复杂度从低到高</p>
<ul>
<li>黑色横线 $O(1)$，常量时间，意味着算法时间并不随数据规模而变化</li>
<li>绿色 $O(log(n))$，对数时间</li>
<li>蓝色 $O(n)$，线性时间，算法时间与数据规模成正比</li>
<li>橙色 $O(n*log(n))$，拟线性时间</li>
<li>红色 $O(n^2)$ 平方时间</li>
<li>黑色朝上 $O(2^n)$ 指数时间</li>
<li>没画出来的 $O(n!)$</li>
</ul>
<p><strong>渐进下界</strong></p>
<p>渐进下界（asymptotic lower bound）：从某个常数 $n_0$开始，$c*g(n)$ 总是位于 $f(n)$ 下方，那么记作 $\Omega(g(n))$</p>
<p><strong>渐进紧界</strong></p>
<p>渐进紧界（asymptotic tight bounds）：从某个常数 $n_0$开始，$f(n)$ 总是在 $c_1<em>g(n)$ 和 $c_2</em>g(n)$ 之间，那么记作 $\Theta(g(n))$</p>
<p><strong>空间复杂度</strong></p>
<p>与时间复杂度类似，一般也使用大 $O$ 表示法来衡量：一个算法执行随数据规模增大，而增长的<strong>额外</strong>空间成本</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">static</span> <span class="type">int</span> <span class="title function_">binarySearchBasic</span><span class="params">(<span class="type">int</span>[] a, <span class="type">int</span> target)</span> &#123;</span><br><span class="line">    <span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> <span class="number">0</span>, j = a.length - <span class="number">1</span>;    <span class="comment">// 设置指针和初值</span></span><br><span class="line">    <span class="keyword">while</span> (i &lt;= j) &#123;                <span class="comment">// i~j 范围内有东西</span></span><br><span class="line">        <span class="type">int</span> <span class="variable">m</span> <span class="operator">=</span> (i + j) &gt;&gt;&gt; <span class="number">1</span>;</span><br><span class="line">        <span class="keyword">if</span>(target &lt; a[m]) &#123;         <span class="comment">// 目标在左边</span></span><br><span class="line">            j = m - <span class="number">1</span>;</span><br><span class="line">        &#125; <span class="keyword">else</span> <span class="keyword">if</span> (a[m] &lt; target) &#123; <span class="comment">// 目标在右边</span></span><br><span class="line">            i = m + <span class="number">1</span>;</span><br><span class="line">        &#125; <span class="keyword">else</span> &#123;                    <span class="comment">// 找到了</span></span><br><span class="line">            <span class="keyword">return</span> m;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> -<span class="number">1</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>



<p><strong>二分查找性能</strong></p>
<p>下面分析二分查找算法的性能</p>
<p>时间复杂度</p>
<ul>
<li>最坏情况：$O(\log n)$</li>
<li>最好情况：如果待查找元素恰好在数组中央，只需要循环一次 $O(1)$</li>
</ul>
<p>空间复杂度</p>
<ul>
<li>需要常数个指针 $i,j,m$，因此额外占用的空间是 $O(1)$</li>
</ul>
<h3 id="二分查找平衡版"><a href="#二分查找平衡版" class="headerlink" title="二分查找平衡版"></a>二分查找平衡版</h3><figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">static</span> <span class="type">int</span> <span class="title function_">binarySearchBalance</span><span class="params">(<span class="type">int</span>[] a, <span class="type">int</span> target)</span> &#123;</span><br><span class="line">    <span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> <span class="number">0</span>, j = a.length;</span><br><span class="line">    <span class="keyword">while</span> (<span class="number">1</span> &lt; j - i) &#123;</span><br><span class="line">        <span class="type">int</span> <span class="variable">m</span> <span class="operator">=</span> (i + j) &gt;&gt;&gt; <span class="number">1</span>;</span><br><span class="line">        <span class="keyword">if</span> (target &lt; a[m]) &#123;</span><br><span class="line">            j = m;</span><br><span class="line">        &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">            i = m;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> (a[i] == target) ? i : -<span class="number">1</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>思想：</p>
<ol>
<li>左闭右开的区间，$i$ 指向的可能是目标，而 $j$ 指向的不是目标</li>
<li>不奢望循环内通过 $m$ 找出目标, 缩小区间直至剩 1 个, 剩下的这个可能就是要找的（通过 $i$）<ul>
<li>$j - i &gt; 1$ 的含义是，在范围内待比较的元素个数 &gt; 1</li>
</ul>
</li>
<li>改变 $i$ 边界时，它指向的可能是目标，因此不能 $m+1$</li>
<li>循环内的平均比较次数减少了</li>
<li>时间复杂度 $\Theta(log(n))$</li>
</ol>
<h3 id="二分查找-Java-版"><a href="#二分查找-Java-版" class="headerlink" title="二分查找 Java 版"></a>二分查找 Java 版</h3><figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">private</span> <span class="keyword">static</span> <span class="type">int</span> <span class="title function_">binarySearch0</span><span class="params">(<span class="type">long</span>[] a, <span class="type">int</span> fromIndex, <span class="type">int</span> toIndex,</span></span><br><span class="line"><span class="params">                                     <span class="type">long</span> key)</span> &#123;</span><br><span class="line">    <span class="type">int</span> <span class="variable">low</span> <span class="operator">=</span> fromIndex;</span><br><span class="line">    <span class="type">int</span> <span class="variable">high</span> <span class="operator">=</span> toIndex - <span class="number">1</span>;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">while</span> (low &lt;= high) &#123;</span><br><span class="line">        <span class="type">int</span> <span class="variable">mid</span> <span class="operator">=</span> (low + high) &gt;&gt;&gt; <span class="number">1</span>;</span><br><span class="line">        <span class="type">long</span> <span class="variable">midVal</span> <span class="operator">=</span> a[mid];</span><br><span class="line"></span><br><span class="line">        <span class="keyword">if</span> (midVal &lt; key)</span><br><span class="line">            low = mid + <span class="number">1</span>;</span><br><span class="line">        <span class="keyword">else</span> <span class="keyword">if</span> (midVal &gt; key)</span><br><span class="line">            high = mid - <span class="number">1</span>;</span><br><span class="line">        <span class="keyword">else</span></span><br><span class="line">            <span class="keyword">return</span> mid; <span class="comment">// key found</span></span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> -(low + <span class="number">1</span>);  <span class="comment">// key not found.</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<ul>
<li>例如 $[1,3,5,6]$ 要插入 $2$ 那么就是找到一个位置，这个位置左侧元素都比它小<ul>
<li>等循环结束，若没找到，low 左侧元素肯定都比 target 小，因此 low 即插入点</li>
</ul>
</li>
<li>插入点取负是为了与找到情况区分</li>
<li>-1 是为了把索引 0 位置的插入点与找到的情况进行区分</li>
</ul>
<h3 id="Leftmost-与-Rightmost"><a href="#Leftmost-与-Rightmost" class="headerlink" title="Leftmost 与 Rightmost"></a>Leftmost 与 Rightmost</h3><p>有时我们希望返回的是最左侧的重复元素，如果用 Basic 二分查找</p>
<ul>
<li><p>对于数组 $[1, 2, 3, 4, 4, 5, 6, 7]$，查找元素4，结果是索引3</p>
</li>
<li><p>对于数组 $[1, 2, 4, 4, 4, 5, 6, 7]$，查找元素4，结果也是索引3，并不是最左侧的元素</p>
</li>
</ul>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">static</span> <span class="type">int</span> <span class="title function_">binarySearchLeftmost1</span><span class="params">(<span class="type">int</span>[] a, <span class="type">int</span> target)</span> &#123;</span><br><span class="line">    <span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> <span class="number">0</span>, j = a.length - <span class="number">1</span>;</span><br><span class="line">    <span class="type">int</span> <span class="variable">candidate</span> <span class="operator">=</span> -<span class="number">1</span>;</span><br><span class="line">    <span class="keyword">while</span> (i &lt;= j) &#123;</span><br><span class="line">        <span class="type">int</span> <span class="variable">m</span> <span class="operator">=</span> (i + j) &gt;&gt;&gt; <span class="number">1</span>;</span><br><span class="line">        <span class="keyword">if</span> (target &lt; a[m]) &#123;</span><br><span class="line">            j = m - <span class="number">1</span>;</span><br><span class="line">        &#125; <span class="keyword">else</span> <span class="keyword">if</span> (a[m] &lt; target) &#123;</span><br><span class="line">            i = m + <span class="number">1</span>;</span><br><span class="line">        &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">            candidate = m; <span class="comment">// 记录候选位置</span></span><br><span class="line">            j = m - <span class="number">1</span>;     <span class="comment">// 继续向左</span></span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> candidate;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>如果希望返回的是最右侧元素</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">static</span> <span class="type">int</span> <span class="title function_">binarySearchRightmost1</span><span class="params">(<span class="type">int</span>[] a, <span class="type">int</span> target)</span> &#123;</span><br><span class="line">    <span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> <span class="number">0</span>, j = a.length - <span class="number">1</span>;</span><br><span class="line">    <span class="type">int</span> <span class="variable">candidate</span> <span class="operator">=</span> -<span class="number">1</span>;</span><br><span class="line">    <span class="keyword">while</span> (i &lt;= j) &#123;</span><br><span class="line">        <span class="type">int</span> <span class="variable">m</span> <span class="operator">=</span> (i + j) &gt;&gt;&gt; <span class="number">1</span>;</span><br><span class="line">        <span class="keyword">if</span> (target &lt; a[m]) &#123;</span><br><span class="line">            j = m - <span class="number">1</span>;</span><br><span class="line">        &#125; <span class="keyword">else</span> <span class="keyword">if</span> (a[m] &lt; target) &#123;</span><br><span class="line">            i = m + <span class="number">1</span>;</span><br><span class="line">        &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">            candidate = m; <span class="comment">// 记录候选位置</span></span><br><span class="line">            i = m + <span class="number">1</span>;	   <span class="comment">// 继续向右</span></span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> candidate;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>



<p><strong>应用</strong></p>
<p>对于 Leftmost 与 Rightmost，可以返回一个比 -1 更有用的值</p>
<p>Leftmost 改为</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">static</span> <span class="type">int</span> <span class="title function_">binarySearchLeftmost</span><span class="params">(<span class="type">int</span>[] a, <span class="type">int</span> target)</span> &#123;</span><br><span class="line">    <span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> <span class="number">0</span>, j = a.length - <span class="number">1</span>;</span><br><span class="line">    <span class="keyword">while</span> (i &lt;= j) &#123;</span><br><span class="line">        <span class="type">int</span> <span class="variable">m</span> <span class="operator">=</span> (i + j) &gt;&gt;&gt; <span class="number">1</span>;</span><br><span class="line">        <span class="keyword">if</span> (target &lt;= a[m]) &#123;</span><br><span class="line">            j = m - <span class="number">1</span>;</span><br><span class="line">        &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">            i = m + <span class="number">1</span>;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> i; </span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<ul>
<li>leftmost 返回值的另一层含义：$\lt target$ 的元素个数</li>
<li>小于等于中间值，都要向左找</li>
</ul>
<p>Rightmost 改为</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">static</span> <span class="type">int</span> <span class="title function_">binarySearchRightmost</span><span class="params">(<span class="type">int</span>[] a, <span class="type">int</span> target)</span> &#123;</span><br><span class="line">    <span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> <span class="number">0</span>, j = a.length - <span class="number">1</span>;</span><br><span class="line">    <span class="keyword">while</span> (i &lt;= j) &#123;</span><br><span class="line">        <span class="type">int</span> <span class="variable">m</span> <span class="operator">=</span> (i + j) &gt;&gt;&gt; <span class="number">1</span>;</span><br><span class="line">        <span class="keyword">if</span> (target &lt; a[m]) &#123;</span><br><span class="line">            j = m - <span class="number">1</span>;</span><br><span class="line">        &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">            i = m + <span class="number">1</span>;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> i - <span class="number">1</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<ul>
<li>大于等于中间值，都要向右找</li>
</ul>
<p>几个名词</p>
<p><img src= "" data-lazy-src="http://rx6zk4j2b.hn-bkt.clouddn.com/blogs/image-20221125174155058.png" alt="image-20221125174155058"></p>
<p><strong>范围查询</strong>：</p>
<ul>
<li>查询 $x \lt 4$，$0 .. leftmost(4) - 1$</li>
<li>查询 $x \leq 4$，$0 .. rightmost(4)$</li>
<li>查询 $4 \lt x$，$rightmost(4) + 1 .. \infty $</li>
<li>查询 $4 \leq x$， $leftmost(4) .. \infty$</li>
<li>查询 $4 \leq x \leq 7$，$leftmost(4) .. rightmost(7)$</li>
<li>查询 $4 \lt x \lt 7$，$rightmost(4)+1 .. leftmost(7)-1$</li>
</ul>
<p><strong>求排名</strong>：$leftmost(target) + 1$</p>
<ul>
<li>$target$ 可以不存在，如：$leftmost(5)+1 &#x3D; 6$</li>
<li>$target$ 也可以存在，如：$leftmost(4)+1 &#x3D; 3$</li>
</ul>
<p><strong>求前任（predecessor）</strong>：$leftmost(target) - 1$</p>
<ul>
<li>$leftmost(3) - 1 &#x3D; 1$，前任 $a_1 &#x3D; 2$</li>
<li>$leftmost(4) - 1 &#x3D; 1$，前任 $a_1 &#x3D; 2$</li>
</ul>
<p><strong>求后任（successor）</strong>：$rightmost(target)+1$</p>
<ul>
<li>$rightmost(5) + 1 &#x3D; 5$，后任 $a_5 &#x3D; 7$</li>
<li>$rightmost(4) + 1 &#x3D; 5$，后任 $a_5 &#x3D; 7$</li>
</ul>
<p><strong>求最近邻居</strong>：</p>
<ul>
<li>前任和后任距离更近者</li>
</ul>
<h1 id="二-基础数据结构"><a href="#二-基础数据结构" class="headerlink" title="二. 基础数据结构"></a>二. 基础数据结构</h1><h2 id="2-1-数组"><a href="#2-1-数组" class="headerlink" title="2.1 数组"></a>2.1 数组</h2><h3 id="概述"><a href="#概述" class="headerlink" title="概述"></a>概述</h3><p><strong>定义</strong></p>
<p>在计算机科学中，数组是由一组元素（值或变量）组成的数据结构，每个元素有至少一个索引或键来标识</p>
<blockquote>
<p>In computer science, an <strong>array</strong> is a data structure consisting of a collection of <em>elements</em> (values or variables), each identified by at least one <em>array index</em> or <em>key</em></p>
</blockquote>
<p>因为数组内的元素是<strong>连续存储</strong>的，所以数组中元素的地址，可以通过其索引计算出来，例如：</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="type">int</span>[] array = &#123;<span class="number">1</span>,<span class="number">2</span>,<span class="number">3</span>,<span class="number">4</span>,<span class="number">5</span>&#125;</span><br></pre></td></tr></table></figure>



<p>知道了数组的<strong>数据</strong>起始地址 $BaseAddress$，就可以由公式 $BaseAddress + i * size$ 计算出索引 $i$ 元素的地址</p>
<ul>
<li>$i$ 即索引，在 Java、C 等语言都是从 0 开始</li>
<li>$size$ 是每个元素占用字节，例如 $int$ 占 $4$，$double$ 占 $8$</li>
</ul>
<p><strong>小测试</strong></p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="type">byte</span>[] array = &#123;<span class="number">1</span>,<span class="number">2</span>,<span class="number">3</span>,<span class="number">4</span>,<span class="number">5</span>&#125;</span><br></pre></td></tr></table></figure>

<p>已知 array 的<strong>数据</strong>的起始地址是 0x7138f94c8，那么元素 3 的地址是什么？</p>
<blockquote>
<p>答：0x7138f94c8 + 2 * 1 &#x3D; 0x7138f94ca</p>
</blockquote>
<p><strong>空间占用</strong></p>
<p>Java 中数组结构为</p>
<ul>
<li>8 字节 markword</li>
<li>4 字节 class 指针（压缩 class 指针的情况）</li>
<li>4 字节 数组大小（决定了数组最大容量是 $2^{32}$）</li>
<li>数组元素 + 对齐字节（java 中所有对象大小都是 8 字节的整数倍[^12]，不足的要用对齐字节补足）</li>
</ul>
<p>例如</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="type">int</span>[] array = &#123;<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>, <span class="number">4</span>, <span class="number">5</span>&#125;;</span><br></pre></td></tr></table></figure>

<p>的大小为 40 个字节，组成如下</p>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">8 + 4 + 4 + 5*4 + 4(alignment)</span><br></pre></td></tr></table></figure>



<p><strong>随机访问性能</strong></p>
<p>即根据索引查找元素，时间复杂度是 $O(1)$</p>
<h3 id="动态数组"><a href="#动态数组" class="headerlink" title="动态数组"></a>动态数组</h3><p><strong>java 版本</strong></p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">DynamicArray</span> <span class="keyword">implements</span> <span class="title class_">Iterable</span>&lt;Integer&gt; &#123;</span><br><span class="line">    <span class="keyword">private</span> <span class="type">int</span> <span class="variable">size</span> <span class="operator">=</span> <span class="number">0</span>; <span class="comment">// 逻辑大小</span></span><br><span class="line">    <span class="keyword">private</span> <span class="type">int</span> <span class="variable">capacity</span> <span class="operator">=</span> <span class="number">8</span>; <span class="comment">// 容量</span></span><br><span class="line">    <span class="keyword">private</span> <span class="type">int</span>[] array = &#123;&#125;;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 向最后位置 [size] 添加元素</span></span><br><span class="line"><span class="comment">     *</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> element 待添加元素</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">addLast</span><span class="params">(<span class="type">int</span> element)</span> &#123;</span><br><span class="line">        add(size, element);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 向 [0 .. size] 位置添加元素</span></span><br><span class="line"><span class="comment">     *</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> index   索引位置</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> element 待添加元素</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">add</span><span class="params">(<span class="type">int</span> index, <span class="type">int</span> element)</span> &#123;</span><br><span class="line">        checkAndGrow();</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 添加逻辑</span></span><br><span class="line">        <span class="keyword">if</span> (index &gt;= <span class="number">0</span> &amp;&amp; index &lt; size) &#123;</span><br><span class="line">            <span class="comment">// 向后挪动, 空出待插入位置</span></span><br><span class="line">            System.arraycopy(array, index,</span><br><span class="line">                    array, index + <span class="number">1</span>, size - index);</span><br><span class="line">        &#125;</span><br><span class="line">        array[index] = element;</span><br><span class="line">        size++;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">void</span> <span class="title function_">checkAndGrow</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="comment">// 容量检查</span></span><br><span class="line">        <span class="keyword">if</span> (size == <span class="number">0</span>) &#123;</span><br><span class="line">            array = <span class="keyword">new</span> <span class="title class_">int</span>[capacity];</span><br><span class="line">        &#125; <span class="keyword">else</span> <span class="keyword">if</span> (size == capacity) &#123;</span><br><span class="line">            <span class="comment">// 进行扩容, 1.5 1.618 2</span></span><br><span class="line">            capacity += capacity &gt;&gt; <span class="number">1</span>;</span><br><span class="line">            <span class="type">int</span>[] newArray = <span class="keyword">new</span> <span class="title class_">int</span>[capacity];</span><br><span class="line">            System.arraycopy(array, <span class="number">0</span>,</span><br><span class="line">                    newArray, <span class="number">0</span>, size);</span><br><span class="line">            array = newArray;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 从 [0 .. size) 范围删除元素</span></span><br><span class="line"><span class="comment">     *</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> index 索引位置</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@return</span> 被删除元素</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">public</span> <span class="type">int</span> <span class="title function_">remove</span><span class="params">(<span class="type">int</span> index)</span> &#123; <span class="comment">// [0..size)</span></span><br><span class="line">        <span class="type">int</span> <span class="variable">removed</span> <span class="operator">=</span> array[index];</span><br><span class="line">        <span class="keyword">if</span> (index &lt; size - <span class="number">1</span>) &#123;</span><br><span class="line">            <span class="comment">// 向前挪动</span></span><br><span class="line">            System.arraycopy(array, index + <span class="number">1</span>,</span><br><span class="line">                    array, index, size - index - <span class="number">1</span>);</span><br><span class="line">        &#125;</span><br><span class="line">        size--;</span><br><span class="line">        <span class="keyword">return</span> removed;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 查询元素</span></span><br><span class="line"><span class="comment">     *</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> index 索引位置, 在 [0..size) 区间内</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@return</span> 该索引位置的元素</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">public</span> <span class="type">int</span> <span class="title function_">get</span><span class="params">(<span class="type">int</span> index)</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> array[index];</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 遍历方法1</span></span><br><span class="line"><span class="comment">     *</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> consumer 遍历要执行的操作, 入参: 每个元素</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">foreach</span><span class="params">(Consumer&lt;Integer&gt; consumer)</span> &#123;</span><br><span class="line">        <span class="keyword">for</span> (<span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> <span class="number">0</span>; i &lt; size; i++) &#123;</span><br><span class="line">            <span class="comment">// 提供 array[i]</span></span><br><span class="line">            <span class="comment">// 返回 void</span></span><br><span class="line">            consumer.accept(array[i]);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 遍历方法2 - 迭代器遍历</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> Iterator&lt;Integer&gt; <span class="title function_">iterator</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">new</span> <span class="title class_">Iterator</span>&lt;Integer&gt;() &#123;</span><br><span class="line">            <span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> <span class="number">0</span>;</span><br><span class="line"></span><br><span class="line">            <span class="meta">@Override</span></span><br><span class="line">            <span class="keyword">public</span> <span class="type">boolean</span> <span class="title function_">hasNext</span><span class="params">()</span> &#123; <span class="comment">// 有没有下一个元素</span></span><br><span class="line">                <span class="keyword">return</span> i &lt; size;</span><br><span class="line">            &#125;</span><br><span class="line"></span><br><span class="line">            <span class="meta">@Override</span></span><br><span class="line">            <span class="keyword">public</span> Integer <span class="title function_">next</span><span class="params">()</span> &#123; <span class="comment">// 返回当前元素,并移动到下一个元素</span></span><br><span class="line">                <span class="keyword">return</span> array[i++];</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 遍历方法3 - stream 遍历</span></span><br><span class="line"><span class="comment">     *</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@return</span> stream 流</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">public</span> IntStream <span class="title function_">stream</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> IntStream.of(Arrays.copyOfRange(array, <span class="number">0</span>, size));</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<ul>
<li>这些方法实现，都简化了 index 的有效性判断，假设输入的 index 都是合法的</li>
</ul>
<p><strong>插入或删除性能</strong></p>
<p>头部位置，时间复杂度是 $O(n)$</p>
<p>中间位置，时间复杂度是 $O(n)$</p>
<p>尾部位置，时间复杂度是 $O(1)$（均摊来说）</p>
<h3 id="二维数组"><a href="#二维数组" class="headerlink" title="二维数组"></a>二维数组</h3><figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="type">int</span>[][] array = &#123;</span><br><span class="line">    &#123;<span class="number">11</span>, <span class="number">12</span>, <span class="number">13</span>, <span class="number">14</span>, <span class="number">15</span>&#125;,</span><br><span class="line">    &#123;<span class="number">21</span>, <span class="number">22</span>, <span class="number">23</span>, <span class="number">24</span>, <span class="number">25</span>&#125;,</span><br><span class="line">    &#123;<span class="number">31</span>, <span class="number">32</span>, <span class="number">33</span>, <span class="number">34</span>, <span class="number">35</span>&#125;,</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure>

<p>内存图如下</p>
<img src= "" data-lazy-src="http://rx6zk4j2b.hn-bkt.clouddn.com/blogs/image-20221104114132056.png" alt="image-20221104114132056" style="zoom:67%;" />

<ul>
<li><p>二维数组占 32 个字节，其中 array[0]，array[1]，array[2] 三个元素分别保存了指向三个一维数组的引用</p>
</li>
<li><p>三个一维数组各占 40 个字节</p>
</li>
<li><p>它们在内层布局上是<strong>连续</strong>的</p>
</li>
</ul>
<p>更一般的，对一个二维数组 $Array[m][n]$</p>
<ul>
<li>$m$ 是外层数组的长度，可以看作 row 行</li>
<li>$n$ 是内层数组的长度，可以看作 column 列</li>
<li>当访问 $Array[i][j]$，$0\leq i \lt m, 0\leq j \lt n$时，就相当于<ul>
<li>先找到第 $i$ 个内层数组（行）</li>
<li>再找到此内层数组中第 $j$ 个元素（列）</li>
</ul>
</li>
</ul>
<p><strong>小测试</strong></p>
<p>Java 环境下（不考虑类指针和引用压缩，此为默认情况），有下面的二维数组</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="type">byte</span>[][] array = &#123;</span><br><span class="line">    &#123;<span class="number">11</span>, <span class="number">12</span>, <span class="number">13</span>, <span class="number">14</span>, <span class="number">15</span>&#125;,</span><br><span class="line">    &#123;<span class="number">21</span>, <span class="number">22</span>, <span class="number">23</span>, <span class="number">24</span>, <span class="number">25</span>&#125;,</span><br><span class="line">    &#123;<span class="number">31</span>, <span class="number">32</span>, <span class="number">33</span>, <span class="number">34</span>, <span class="number">35</span>&#125;,</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure>

<p>已知 array <strong>对象</strong>起始地址是 0x1000，那么 23 这个元素的地址是什么？</p>
<blockquote>
<p>答：</p>
<ul>
<li>起始地址 0x1000</li>
<li>外层数组大小：16字节对象头 + 3元素 * 每个引用4字节 + 4 对齐字节 &#x3D; 32 &#x3D; 0x20</li>
<li>第一个内层数组大小：16字节对象头 + 5元素 * 每个byte1字节 + 3 对齐字节 &#x3D; 24 &#x3D; 0x18</li>
<li>第二个内层数组，16字节对象头 &#x3D; 0x10，待查找元素索引为 2</li>
<li>最后结果 &#x3D; 0x1000 + 0x20 + 0x18 + 0x10 + 2*1 &#x3D; 0x104a</li>
</ul>
</blockquote>
<h3 id="局部性原理"><a href="#局部性原理" class="headerlink" title="局部性原理"></a>局部性原理</h3><p>这里只讨论空间局部性</p>
<ul>
<li>cpu 读取内存（速度慢）数据后，会将其放入高速缓存（速度快）当中，如果后来的计算再用到此数据，在缓存中能读到的话，就不必读内存了</li>
<li>缓存的最小存储单位是缓存行（cache line），一般是 64 bytes，一次读的数据少了不划算啊，因此最少读 64 bytes 填满一个缓存行，因此读入某个数据时也会读取其<strong>临近的数据</strong>，这就是所谓<strong>空间局部性</strong></li>
</ul>
<p><strong>对效率的影响</strong></p>
<p>比较下面 ij 和 ji 两个方法的执行效率</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="type">int</span> <span class="variable">rows</span> <span class="operator">=</span> <span class="number">1000000</span>;</span><br><span class="line"><span class="type">int</span> <span class="variable">columns</span> <span class="operator">=</span> <span class="number">14</span>;</span><br><span class="line"><span class="type">int</span>[][] a = <span class="keyword">new</span> <span class="title class_">int</span>[rows][columns];</span><br><span class="line"></span><br><span class="line"><span class="type">StopWatch</span> <span class="variable">sw</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">StopWatch</span>();</span><br><span class="line">sw.start(<span class="string">&quot;ij&quot;</span>);</span><br><span class="line">ij(a, rows, columns);</span><br><span class="line">sw.stop();</span><br><span class="line">sw.start(<span class="string">&quot;ji&quot;</span>);</span><br><span class="line">ji(a, rows, columns);</span><br><span class="line">sw.stop();</span><br><span class="line">System.out.println(sw.prettyPrint());</span><br></pre></td></tr></table></figure>

<p>ij 方法</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">ij</span><span class="params">(<span class="type">int</span>[][] a, <span class="type">int</span> rows, <span class="type">int</span> columns)</span> &#123;</span><br><span class="line">    <span class="type">long</span> <span class="variable">sum</span> <span class="operator">=</span> <span class="number">0L</span>;</span><br><span class="line">    <span class="keyword">for</span> (<span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> <span class="number">0</span>; i &lt; rows; i++) &#123;</span><br><span class="line">        <span class="keyword">for</span> (<span class="type">int</span> <span class="variable">j</span> <span class="operator">=</span> <span class="number">0</span>; j &lt; columns; j++) &#123;</span><br><span class="line">            sum += a[i][j];</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    System.out.println(sum);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>ji 方法</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">ji</span><span class="params">(<span class="type">int</span>[][] a, <span class="type">int</span> rows, <span class="type">int</span> columns)</span> &#123;</span><br><span class="line">    <span class="type">long</span> <span class="variable">sum</span> <span class="operator">=</span> <span class="number">0L</span>;</span><br><span class="line">    <span class="keyword">for</span> (<span class="type">int</span> <span class="variable">j</span> <span class="operator">=</span> <span class="number">0</span>; j &lt; columns; j++) &#123;</span><br><span class="line">        <span class="keyword">for</span> (<span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> <span class="number">0</span>; i &lt; rows; i++) &#123;</span><br><span class="line">            sum += a[i][j];</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    System.out.println(sum);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>执行结果</p>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">0</span><br><span class="line">0</span><br><span class="line">StopWatch &#x27;&#x27;: running time = 96283300 ns</span><br><span class="line">---------------------------------------------</span><br><span class="line">ns         %     Task name</span><br><span class="line">---------------------------------------------</span><br><span class="line">016196200  017%  ij</span><br><span class="line">080087100  083%  ji</span><br></pre></td></tr></table></figure>

<p>可以看到 ij 的效率比 ji 快很多，为什么呢？</p>
<ul>
<li>缓存是有限的，当新数据来了后，一些旧的缓存行数据就会被覆盖</li>
<li>如果不能充分利用缓存的数据，就会造成效率低下</li>
</ul>
<p>以 ji 执行为例，第一次内循环要读入 $[0,0]$ 这条数据，由于局部性原理，读入 $[0,0]$ 的同时也读入了 $[0,1] … [0,13]$，如图所示</p>
<p><img src= "" data-lazy-src="http://rx6zk4j2b.hn-bkt.clouddn.com/blogs/image-20221104164329026.png" alt="image-20221104164329026"></p>
<p>但很遗憾，第二次内循环要的是 $[1,0]$ 这条数据，缓存中没有，于是再读入了下图的数据</p>
<p><img src= "" data-lazy-src="http://rx6zk4j2b.hn-bkt.clouddn.com/blogs/image-20221104164716282.png" alt="image-20221104164716282"></p>
<p>这显然是一种浪费，因为 $[0,1] … [0,13]$ 包括 $[1,1] … [1,13]$ 这些数据虽然读入了缓存，却没有及时用上，而缓存的大小是有限的，等执行到第九次内循环时</p>
<p><img src= "" data-lazy-src="http://rx6zk4j2b.hn-bkt.clouddn.com/blogs/image-20221104164947154.png" alt="image-20221104164947154"></p>
<p>缓存的第一行数据已经被新的数据 $[8,0] … [8,13]$ 覆盖掉了，以后如果再想读，比如 $[0,1]$，又得到内存去读了</p>
<p>同理可以分析 ij 函数则能充分利用局部性原理加载到的缓存数据</p>
<p><strong>举一反三</strong></p>
<ol>
<li><p>I&#x2F;O 读写时同样可以体现局部性原理</p>
</li>
<li><p>数组可以充分利用局部性原理，那么链表呢？</p>
<blockquote>
<p>答：链表不行，因为链表的元素并非相邻存储</p>
</blockquote>
</li>
</ol>
<h3 id="越界检查"><a href="#越界检查" class="headerlink" title="越界检查"></a>越界检查</h3><p>java 中对数组元素的读写都有越界检查，类似于下面的代码</p>
<figure class="highlight c++"><table><tr><td class="code"><pre><span class="line"><span class="function"><span class="type">bool</span> <span class="title">is_within_bounds</span><span class="params">(<span class="type">int</span> index)</span> <span class="type">const</span>        </span></span><br><span class="line"><span class="function"></span>&#123; </span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span> &lt;= index &amp;&amp; index &lt; <span class="built_in">length</span>(); </span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<ul>
<li>代码位置：<code>openjdk\src\hotspot\share\oops\arrayOop.hpp</code></li>
</ul>
<p>只不过此检查代码，不需要由程序员自己来调用，JVM 会帮我们调用</p>
<h2 id="2-2-链表"><a href="#2-2-链表" class="headerlink" title="2.2 链表"></a>2.2 链表</h2><h3 id="概述-1"><a href="#概述-1" class="headerlink" title="概述"></a>概述</h3><p><strong>定义</strong></p>
<p>在计算机科学中，链表是数据元素的线性集合，其每个元素都指向下一个元素，元素存储上并不连续</p>
<blockquote>
<p>In computer science, a <strong>linked list</strong> is a linear collection of data elements whose order is not given by their physical placement in memory. Instead, each element points to the next. </p>
</blockquote>
<p>可以分类为[^5]</p>
<ul>
<li>单向链表，每个元素只知道其下一个元素是谁</li>
</ul>
<p><img src= "" data-lazy-src="http://rx6zk4j2b.hn-bkt.clouddn.com/blogs/image-20221110083407176.png" alt="image-20221110083407176"></p>
<ul>
<li>双向链表，每个元素知道其上一个元素和下一个元素</li>
</ul>
<p><img src= "" data-lazy-src="http://rx6zk4j2b.hn-bkt.clouddn.com/blogs/image-20221110083427372.png" alt="image-20221110083427372"></p>
<ul>
<li>循环链表，通常的链表尾节点 tail 指向的都是 null，而循环链表的 tail 指向的是头节点 head</li>
</ul>
<p><img src= "" data-lazy-src="http://rx6zk4j2b.hn-bkt.clouddn.com/blogs/image-20221110083538273.png" alt="image-20221110083538273"></p>
<p>链表内还有一种特殊的节点称为哨兵（Sentinel）节点，也叫做哑元（ Dummy）节点，它不存储数据，通常用作头尾，用来简化边界判断，如下图所示</p>
<p><img src= "" data-lazy-src="http://rx6zk4j2b.hn-bkt.clouddn.com/blogs/image-20221110084611550.png" alt="image-20221110084611550"></p>
<p><strong>随机访问性能</strong></p>
<p>根据 index 查找，时间复杂度 $O(n)$</p>
<p><strong>插入或删除性能</strong></p>
<ul>
<li>起始位置：$O(1)$</li>
<li>结束位置：如果已知 tail 尾节点是 $O(1)$，不知道 tail 尾节点是 $O(n)$</li>
<li>中间位置：根据 index 查找时间 + $O(1)$</li>
</ul>
<h3 id="单向链表"><a href="#单向链表" class="headerlink" title="单向链表"></a>单向链表</h3><p>根据单向链表的定义，首先定义一个存储 value 和 next 指针的类 Node，和一个描述头部节点的引用</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">SinglyLinkedList</span> &#123;</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">private</span> Node head; <span class="comment">// 头部节点</span></span><br><span class="line">    </span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">class</span> <span class="title class_">Node</span> &#123; <span class="comment">// 节点类</span></span><br><span class="line">        <span class="type">int</span> value;</span><br><span class="line">        Node next;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">public</span> <span class="title function_">Node</span><span class="params">(<span class="type">int</span> value, Node next)</span> &#123;</span><br><span class="line">            <span class="built_in">this</span>.value = value;</span><br><span class="line">            <span class="built_in">this</span>.next = next;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<ul>
<li>Node 定义为内部类，是为了对外<strong>隐藏</strong>实现细节，没必要让类的使用者关心 Node 结构</li>
<li>定义为 static 内部类，是因为 Node <strong>不需要</strong>与 SinglyLinkedList 实例相关，多个 SinglyLinkedList实例能共用 Node 类定义</li>
</ul>
<p><strong>头部添加</strong></p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">SinglyLinkedList</span> &#123;</span><br><span class="line">    <span class="comment">// ...</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">addFirst</span><span class="params">(<span class="type">int</span> value)</span> &#123;</span><br><span class="line">		<span class="built_in">this</span>.head = <span class="keyword">new</span> <span class="title class_">Node</span>(value, <span class="built_in">this</span>.head);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<ul>
<li>如果 this.head &#x3D;&#x3D; null，新增节点指向 null，并作为新的 this.head</li>
<li>如果 this.head !&#x3D; null，新增节点指向原来的 this.head，并作为新的 this.head<ul>
<li>注意赋值操作执行顺序是从右到左</li>
</ul>
</li>
</ul>
<p><strong>while 遍历</strong></p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">SinglyLinkedList</span> &#123;</span><br><span class="line">    <span class="comment">// ...</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">loop</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="type">Node</span> <span class="variable">curr</span> <span class="operator">=</span> <span class="built_in">this</span>.head;</span><br><span class="line">        <span class="keyword">while</span> (curr != <span class="literal">null</span>) &#123;</span><br><span class="line">            <span class="comment">// 做一些事</span></span><br><span class="line">            curr = curr.next;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>



<p><strong>for 遍历</strong></p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">SinglyLinkedList</span> &#123;</span><br><span class="line">    <span class="comment">// ...</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">loop</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">for</span> (<span class="type">Node</span> <span class="variable">curr</span> <span class="operator">=</span> <span class="built_in">this</span>.head; curr != <span class="literal">null</span>; curr = curr.next) &#123;</span><br><span class="line">            <span class="comment">// 做一些事</span></span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<ul>
<li>以上两种遍历都可以把<strong>要做的事</strong>以 Consumer 函数的方式传递进来<ul>
<li>Consumer 的规则是<strong>一个参数</strong>，<strong>无返回值</strong>，因此像 System.out::println 方法等都是 Consumer</li>
<li>调用 Consumer 时，将当前节点 curr.value 作为参数传递给它</li>
</ul>
</li>
</ul>
<p><strong>迭代器遍历</strong></p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">SinglyLinkedList</span> <span class="keyword">implements</span> <span class="title class_">Iterable</span>&lt;Integer&gt; &#123;</span><br><span class="line">    <span class="comment">// ...</span></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">class</span> <span class="title class_">NodeIterator</span> <span class="keyword">implements</span> <span class="title class_">Iterator</span>&lt;Integer&gt; &#123;</span><br><span class="line">        <span class="type">Node</span> <span class="variable">curr</span> <span class="operator">=</span> head;</span><br><span class="line">        </span><br><span class="line">        <span class="keyword">public</span> <span class="type">boolean</span> <span class="title function_">hasNext</span><span class="params">()</span> &#123;</span><br><span class="line">            <span class="keyword">return</span> curr != <span class="literal">null</span>;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">public</span> Integer <span class="title function_">next</span><span class="params">()</span> &#123;</span><br><span class="line">            <span class="type">int</span> <span class="variable">value</span> <span class="operator">=</span> curr.value;</span><br><span class="line">            curr = curr.next;</span><br><span class="line">            <span class="keyword">return</span> value;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">public</span> Iterator&lt;Integer&gt; <span class="title function_">iterator</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">new</span> <span class="title class_">NodeIterator</span>();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<ul>
<li>hasNext 用来判断是否还有必要调用 next</li>
<li>next 做两件事<ul>
<li>返回当前节点的 value</li>
<li>指向下一个节点</li>
</ul>
</li>
<li>NodeIterator 要定义为<strong>非 static 内部类</strong>，是因为它与 SinglyLinkedList 实例相关，是对某个 SinglyLinkedList 实例的迭代</li>
</ul>
<p><strong>递归遍历</strong></p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">SinglyLinkedList</span> <span class="keyword">implements</span> <span class="title class_">Iterable</span>&lt;Integer&gt; &#123;</span><br><span class="line">    <span class="comment">// ...</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">loop</span><span class="params">()</span> &#123;</span><br><span class="line">        recursion(<span class="built_in">this</span>.head);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">void</span> <span class="title function_">recursion</span><span class="params">(Node curr)</span> &#123;</span><br><span class="line">        <span class="keyword">if</span> (curr == <span class="literal">null</span>) &#123;</span><br><span class="line">            <span class="keyword">return</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="comment">// 前面做些事</span></span><br><span class="line">        recursion(curr.next);</span><br><span class="line">        <span class="comment">// 后面做些事</span></span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>



<p><strong>尾部添加</strong></p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">SinglyLinkedList</span> &#123;</span><br><span class="line">    <span class="comment">// ...</span></span><br><span class="line">    <span class="keyword">private</span> Node <span class="title function_">findLast</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">if</span> (<span class="built_in">this</span>.head == <span class="literal">null</span>) &#123;</span><br><span class="line">            <span class="keyword">return</span> <span class="literal">null</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        Node curr;</span><br><span class="line">        <span class="keyword">for</span> (curr = <span class="built_in">this</span>.head; curr.next != <span class="literal">null</span>; ) &#123;</span><br><span class="line">            curr = curr.next;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> curr;</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">addLast</span><span class="params">(<span class="type">int</span> value)</span> &#123;</span><br><span class="line">        <span class="type">Node</span> <span class="variable">last</span> <span class="operator">=</span> findLast();</span><br><span class="line">        <span class="keyword">if</span> (last == <span class="literal">null</span>) &#123;</span><br><span class="line">            addFirst(value);</span><br><span class="line">            <span class="keyword">return</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        last.next = <span class="keyword">new</span> <span class="title class_">Node</span>(value, <span class="literal">null</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<ul>
<li>注意，找最后一个节点，终止条件是 curr.next &#x3D;&#x3D; null </li>
<li>分成两个方法是为了代码清晰，而且 findLast() 之后还能复用</li>
</ul>
<p><strong>尾部添加多个</strong></p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">SinglyLinkedList</span> &#123;</span><br><span class="line">    <span class="comment">// ...</span></span><br><span class="line">	<span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">addLast</span><span class="params">(<span class="type">int</span> first, <span class="type">int</span>... rest)</span> &#123;</span><br><span class="line">        </span><br><span class="line">        <span class="type">Node</span> <span class="variable">sublist</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Node</span>(first, <span class="literal">null</span>);</span><br><span class="line">        <span class="type">Node</span> <span class="variable">curr</span> <span class="operator">=</span> sublist;</span><br><span class="line">        <span class="keyword">for</span> (<span class="type">int</span> value : rest) &#123;</span><br><span class="line">            curr.next = <span class="keyword">new</span> <span class="title class_">Node</span>(value, <span class="literal">null</span>);</span><br><span class="line">            curr = curr.next;</span><br><span class="line">        &#125;</span><br><span class="line">        </span><br><span class="line">        <span class="type">Node</span> <span class="variable">last</span> <span class="operator">=</span> findLast();</span><br><span class="line">        <span class="keyword">if</span> (last == <span class="literal">null</span>) &#123;</span><br><span class="line">            <span class="built_in">this</span>.head = sublist;</span><br><span class="line">            <span class="keyword">return</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        last.next = sublist;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<ul>
<li>先串成一串 sublist</li>
<li>再作为一个整体添加</li>
</ul>
<p><strong>根据索引获取</strong></p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">SinglyLinkedList</span> &#123;</span><br><span class="line">    <span class="comment">// ...</span></span><br><span class="line">	<span class="keyword">private</span> Node <span class="title function_">findNode</span><span class="params">(<span class="type">int</span> index)</span> &#123;</span><br><span class="line">        <span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> <span class="number">0</span>;</span><br><span class="line">        <span class="keyword">for</span> (<span class="type">Node</span> <span class="variable">curr</span> <span class="operator">=</span> <span class="built_in">this</span>.head; curr != <span class="literal">null</span>; curr = curr.next, i++) &#123;</span><br><span class="line">            <span class="keyword">if</span> (index == i) &#123;</span><br><span class="line">                <span class="keyword">return</span> curr;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> <span class="literal">null</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">private</span> IllegalArgumentException <span class="title function_">illegalIndex</span><span class="params">(<span class="type">int</span> index)</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">new</span> <span class="title class_">IllegalArgumentException</span>(String.format(<span class="string">&quot;index [%d] 不合法%n&quot;</span>, index));</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">public</span> <span class="type">int</span> <span class="title function_">get</span><span class="params">(<span class="type">int</span> index)</span> &#123;</span><br><span class="line">        <span class="type">Node</span> <span class="variable">node</span> <span class="operator">=</span> findNode(index);</span><br><span class="line">        <span class="keyword">if</span> (node != <span class="literal">null</span>) &#123;</span><br><span class="line">            <span class="keyword">return</span> node.value;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">throw</span> illegalIndex(index);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<ul>
<li>同样，分方法可以实现复用</li>
</ul>
<p><strong>插入</strong></p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">SinglyLinkedList</span> &#123;</span><br><span class="line">    <span class="comment">// ...</span></span><br><span class="line">	<span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">insert</span><span class="params">(<span class="type">int</span> index, <span class="type">int</span> value)</span> &#123;</span><br><span class="line">        <span class="keyword">if</span> (index == <span class="number">0</span>) &#123;</span><br><span class="line">            addFirst(value);</span><br><span class="line">            <span class="keyword">return</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="type">Node</span> <span class="variable">prev</span> <span class="operator">=</span> findNode(index - <span class="number">1</span>); <span class="comment">// 找到上一个节点</span></span><br><span class="line">        <span class="keyword">if</span> (prev == <span class="literal">null</span>) &#123; <span class="comment">// 找不到</span></span><br><span class="line">            <span class="keyword">throw</span> illegalIndex(index);</span><br><span class="line">        &#125;</span><br><span class="line">        prev.next = <span class="keyword">new</span> <span class="title class_">Node</span>(value, prev.next);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<ul>
<li>插入包括下面的删除，都必须找到上一个节点</li>
</ul>
<p><strong>删除</strong></p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">SinglyLinkedList</span> &#123;</span><br><span class="line">    <span class="comment">// ...</span></span><br><span class="line">	<span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">remove</span><span class="params">(<span class="type">int</span> index)</span> &#123;</span><br><span class="line">        <span class="keyword">if</span> (index == <span class="number">0</span>) &#123;</span><br><span class="line">            <span class="keyword">if</span> (<span class="built_in">this</span>.head != <span class="literal">null</span>) &#123;</span><br><span class="line">                <span class="built_in">this</span>.head = <span class="built_in">this</span>.head.next;</span><br><span class="line">                <span class="keyword">return</span>;</span><br><span class="line">            &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">                <span class="keyword">throw</span> illegalIndex(index);</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="type">Node</span> <span class="variable">prev</span> <span class="operator">=</span> findNode(index - <span class="number">1</span>);</span><br><span class="line">        Node curr;</span><br><span class="line">        <span class="keyword">if</span> (prev != <span class="literal">null</span> &amp;&amp; (curr = prev.next) != <span class="literal">null</span>) &#123;</span><br><span class="line">            prev.next = curr.next;</span><br><span class="line">        &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">            <span class="keyword">throw</span> illegalIndex(index);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<ul>
<li>第一个 if 块对应着 removeFirst 情况</li>
<li>最后一个 if 块对应着至少得两个节点的情况<ul>
<li>不仅仅判断上一个节点非空，还要保证当前节点非空</li>
</ul>
</li>
</ul>
<h3 id="单向链表（带哨兵）"><a href="#单向链表（带哨兵）" class="headerlink" title="单向链表（带哨兵）"></a>单向链表（带哨兵）</h3><p>观察之前单向链表的实现，发现每个方法内几乎都有判断是不是 head 这样的代码，能不能简化呢？</p>
<p>用一个不参与数据存储的特殊 Node 作为哨兵，它一般被称为哨兵或哑元，拥有哨兵节点的链表称为带头链表</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">SinglyLinkedListSentinel</span> &#123;</span><br><span class="line">    <span class="comment">// ...</span></span><br><span class="line">    <span class="keyword">private</span> <span class="type">Node</span> <span class="variable">head</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Node</span>(Integer.MIN_VALUE, <span class="literal">null</span>);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<ul>
<li>具体存什么值无所谓，因为不会用到它的值</li>
</ul>
<p>加入哨兵节点后，代码会变得比较简单，先看几个工具方法</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">SinglyLinkedListSentinel</span> &#123;</span><br><span class="line">    <span class="comment">// ...</span></span><br><span class="line">    </span><br><span class="line">    <span class="comment">// 根据索引获取节点</span></span><br><span class="line">    <span class="keyword">private</span> Node <span class="title function_">findNode</span><span class="params">(<span class="type">int</span> index)</span> &#123;</span><br><span class="line">        <span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> -<span class="number">1</span>;</span><br><span class="line">        <span class="keyword">for</span> (<span class="type">Node</span> <span class="variable">curr</span> <span class="operator">=</span> <span class="built_in">this</span>.head; curr != <span class="literal">null</span>; curr = curr.next, i++) &#123;</span><br><span class="line">            <span class="keyword">if</span> (i == index) &#123;</span><br><span class="line">                <span class="keyword">return</span> curr;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> <span class="literal">null</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">// 获取最后一个节点</span></span><br><span class="line">    <span class="keyword">private</span> Node <span class="title function_">findLast</span><span class="params">()</span> &#123;</span><br><span class="line">        Node curr;</span><br><span class="line">        <span class="keyword">for</span> (curr = <span class="built_in">this</span>.head; curr.next != <span class="literal">null</span>; ) &#123;</span><br><span class="line">            curr = curr.next;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> curr;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<ul>
<li>findNode 与之前类似，只是 i 初始值设置为 -1 对应哨兵，实际传入的 index 也是 $[-1, \infty)$</li>
<li>findLast 绝不会返回 null 了，就算没有其它节点，也会返回哨兵作为最后一个节点</li>
</ul>
<p>这样，代码简化为</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">SinglyLinkedListSentinel</span> &#123;</span><br><span class="line">    <span class="comment">// ...</span></span><br><span class="line">    </span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">addLast</span><span class="params">(<span class="type">int</span> value)</span> &#123;</span><br><span class="line">        <span class="type">Node</span> <span class="variable">last</span> <span class="operator">=</span> findLast();</span><br><span class="line">        <span class="comment">/*</span></span><br><span class="line"><span class="comment">        改动前</span></span><br><span class="line"><span class="comment">        if (last == null) &#123;</span></span><br><span class="line"><span class="comment">            this.head = new Node(value, null);</span></span><br><span class="line"><span class="comment">            return;</span></span><br><span class="line"><span class="comment">        &#125;</span></span><br><span class="line"><span class="comment">        */</span></span><br><span class="line">        last.next = <span class="keyword">new</span> <span class="title class_">Node</span>(value, <span class="literal">null</span>);</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">insert</span><span class="params">(<span class="type">int</span> index, <span class="type">int</span> value)</span> &#123;</span><br><span class="line">        <span class="comment">/*</span></span><br><span class="line"><span class="comment">        改动前</span></span><br><span class="line"><span class="comment">        if (index == 0) &#123;</span></span><br><span class="line"><span class="comment">            this.head = new Node(value, this.head);</span></span><br><span class="line"><span class="comment">            return;</span></span><br><span class="line"><span class="comment">        &#125;</span></span><br><span class="line"><span class="comment">        */</span></span><br><span class="line">        <span class="comment">// index 传入 0 时，返回的是哨兵</span></span><br><span class="line">        <span class="type">Node</span> <span class="variable">prev</span> <span class="operator">=</span> findNode(index - <span class="number">1</span>);</span><br><span class="line">        <span class="keyword">if</span> (prev != <span class="literal">null</span>) &#123;</span><br><span class="line">            prev.next = <span class="keyword">new</span> <span class="title class_">Node</span>(value, prev.next);</span><br><span class="line">        &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">            <span class="keyword">throw</span> illegalIndex(index);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">remove</span><span class="params">(<span class="type">int</span> index)</span> &#123;</span><br><span class="line">        <span class="comment">/*</span></span><br><span class="line"><span class="comment">        改动前</span></span><br><span class="line"><span class="comment">        if (index == 0) &#123;</span></span><br><span class="line"><span class="comment">            if (this.head != null) &#123;</span></span><br><span class="line"><span class="comment">                this.head = this.head.next;</span></span><br><span class="line"><span class="comment">                return;</span></span><br><span class="line"><span class="comment">            &#125; else &#123;</span></span><br><span class="line"><span class="comment">                throw illegalIndex(index);</span></span><br><span class="line"><span class="comment">            &#125;</span></span><br><span class="line"><span class="comment">        &#125;</span></span><br><span class="line"><span class="comment">        */</span></span><br><span class="line">        <span class="comment">// index 传入 0 时，返回的是哨兵</span></span><br><span class="line">        <span class="type">Node</span> <span class="variable">prev</span> <span class="operator">=</span> findNode(index - <span class="number">1</span>);</span><br><span class="line">        Node curr;</span><br><span class="line">        <span class="keyword">if</span> (prev != <span class="literal">null</span> &amp;&amp; (curr = prev.next) != <span class="literal">null</span>) &#123;</span><br><span class="line">            prev.next = curr.next;</span><br><span class="line">        &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">            <span class="keyword">throw</span> illegalIndex(index);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">addFirst</span><span class="params">(<span class="type">int</span> value)</span> &#123;</span><br><span class="line">        <span class="comment">/*</span></span><br><span class="line"><span class="comment">        改动前</span></span><br><span class="line"><span class="comment">        this.head = new Node(value, this.head);</span></span><br><span class="line"><span class="comment">        */</span></span><br><span class="line">		<span class="built_in">this</span>.head.next = <span class="keyword">new</span> <span class="title class_">Node</span>(value, <span class="built_in">this</span>.head.next);</span><br><span class="line">        <span class="comment">// 也可以视为 insert 的特例, 即 insert(0, value);</span></span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<ul>
<li>对于删除，前面说了【最后一个 if 块对应着至少得两个节点的情况】，现在有了哨兵，就凑足了两个节点</li>
</ul>
<h3 id="双向链表（带哨兵）"><a href="#双向链表（带哨兵）" class="headerlink" title="双向链表（带哨兵）"></a>双向链表（带哨兵）</h3><figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">DoublyLinkedListSentinel</span> <span class="keyword">implements</span> <span class="title class_">Iterable</span>&lt;Integer&gt; &#123;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">final</span> Node head;</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">final</span> Node tail;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="title function_">DoublyLinkedListSentinel</span><span class="params">()</span> &#123;</span><br><span class="line">        head = <span class="keyword">new</span> <span class="title class_">Node</span>(<span class="literal">null</span>, <span class="number">666</span>, <span class="literal">null</span>);</span><br><span class="line">        tail = <span class="keyword">new</span> <span class="title class_">Node</span>(<span class="literal">null</span>, <span class="number">888</span>, <span class="literal">null</span>);</span><br><span class="line">        head.next = tail;</span><br><span class="line">        tail.prev = head;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> Node <span class="title function_">findNode</span><span class="params">(<span class="type">int</span> index)</span> &#123;</span><br><span class="line">        <span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> -<span class="number">1</span>;</span><br><span class="line">        <span class="keyword">for</span> (<span class="type">Node</span> <span class="variable">p</span> <span class="operator">=</span> head; p != tail; p = p.next, i++) &#123;</span><br><span class="line">            <span class="keyword">if</span> (i == index) &#123;</span><br><span class="line">                <span class="keyword">return</span> p;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> <span class="literal">null</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">addFirst</span><span class="params">(<span class="type">int</span> value)</span> &#123;</span><br><span class="line">        insert(<span class="number">0</span>, value);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">removeFirst</span><span class="params">()</span> &#123;</span><br><span class="line">        remove(<span class="number">0</span>);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">addLast</span><span class="params">(<span class="type">int</span> value)</span> &#123;</span><br><span class="line">        <span class="type">Node</span> <span class="variable">prev</span> <span class="operator">=</span> tail.prev;</span><br><span class="line">        <span class="type">Node</span> <span class="variable">added</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Node</span>(prev, value, tail);</span><br><span class="line">        prev.next = added;</span><br><span class="line">        tail.prev = added;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">removeLast</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="type">Node</span> <span class="variable">removed</span> <span class="operator">=</span> tail.prev;</span><br><span class="line">        <span class="keyword">if</span> (removed == head) &#123;</span><br><span class="line">            <span class="keyword">throw</span> illegalIndex(<span class="number">0</span>);</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="type">Node</span> <span class="variable">prev</span> <span class="operator">=</span> removed.prev;</span><br><span class="line">        prev.next = tail;</span><br><span class="line">        tail.prev = prev;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">insert</span><span class="params">(<span class="type">int</span> index, <span class="type">int</span> value)</span> &#123;</span><br><span class="line">        <span class="type">Node</span> <span class="variable">prev</span> <span class="operator">=</span> findNode(index - <span class="number">1</span>);</span><br><span class="line">        <span class="keyword">if</span> (prev == <span class="literal">null</span>) &#123;</span><br><span class="line">            <span class="keyword">throw</span> illegalIndex(index);</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="type">Node</span> <span class="variable">next</span> <span class="operator">=</span> prev.next;</span><br><span class="line">        <span class="type">Node</span> <span class="variable">inserted</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Node</span>(prev, value, next);</span><br><span class="line">        prev.next = inserted;</span><br><span class="line">        next.prev = inserted;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">remove</span><span class="params">(<span class="type">int</span> index)</span> &#123;</span><br><span class="line">        <span class="type">Node</span> <span class="variable">prev</span> <span class="operator">=</span> findNode(index - <span class="number">1</span>);</span><br><span class="line">        <span class="keyword">if</span> (prev == <span class="literal">null</span>) &#123;</span><br><span class="line">            <span class="keyword">throw</span> illegalIndex(index);</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="type">Node</span> <span class="variable">removed</span> <span class="operator">=</span> prev.next;</span><br><span class="line">        <span class="keyword">if</span> (removed == tail) &#123;</span><br><span class="line">            <span class="keyword">throw</span> illegalIndex(index);</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="type">Node</span> <span class="variable">next</span> <span class="operator">=</span> removed.next;</span><br><span class="line">        prev.next = next;</span><br><span class="line">        next.prev = prev;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> IllegalArgumentException <span class="title function_">illegalIndex</span><span class="params">(<span class="type">int</span> index)</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">new</span> <span class="title class_">IllegalArgumentException</span>(</span><br><span class="line">                String.format(<span class="string">&quot;index [%d] 不合法%n&quot;</span>, index));</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> Iterator&lt;Integer&gt; <span class="title function_">iterator</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">new</span> <span class="title class_">Iterator</span>&lt;Integer&gt;() &#123;</span><br><span class="line">            <span class="type">Node</span> <span class="variable">p</span> <span class="operator">=</span> head.next;</span><br><span class="line"></span><br><span class="line">            <span class="meta">@Override</span></span><br><span class="line">            <span class="keyword">public</span> <span class="type">boolean</span> <span class="title function_">hasNext</span><span class="params">()</span> &#123;</span><br><span class="line">                <span class="keyword">return</span> p != tail;</span><br><span class="line">            &#125;</span><br><span class="line"></span><br><span class="line">            <span class="meta">@Override</span></span><br><span class="line">            <span class="keyword">public</span> Integer <span class="title function_">next</span><span class="params">()</span> &#123;</span><br><span class="line">                <span class="type">int</span> <span class="variable">value</span> <span class="operator">=</span> p.value;</span><br><span class="line">                p = p.next;</span><br><span class="line">                <span class="keyword">return</span> value;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">static</span> <span class="keyword">class</span> <span class="title class_">Node</span> &#123;</span><br><span class="line">        Node prev;</span><br><span class="line">        <span class="type">int</span> value;</span><br><span class="line">        Node next;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">public</span> <span class="title function_">Node</span><span class="params">(Node prev, <span class="type">int</span> value, Node next)</span> &#123;</span><br><span class="line">            <span class="built_in">this</span>.prev = prev;</span><br><span class="line">            <span class="built_in">this</span>.value = value;</span><br><span class="line">            <span class="built_in">this</span>.next = next;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>



<h3 id="环形链表（带哨兵）"><a href="#环形链表（带哨兵）" class="headerlink" title="环形链表（带哨兵）"></a>环形链表（带哨兵）</h3><p>双向环形链表带哨兵，这时哨兵<strong>既作为头，也作为尾</strong></p>
<p><img src= "" data-lazy-src="http://rx6zk4j2b.hn-bkt.clouddn.com/blogs/image-20221229144232651.png" alt="image-20221229144232651"></p>
<p><img src= "" data-lazy-src="http://rx6zk4j2b.hn-bkt.clouddn.com/blogs/image-20221229143756065.png" alt="image-20221229143756065"></p>
<p><img src= "" data-lazy-src="http://rx6zk4j2b.hn-bkt.clouddn.com/blogs/image-20221229153338425.png" alt="image-20221229153338425"></p>
<p><img src= "" data-lazy-src="http://rx6zk4j2b.hn-bkt.clouddn.com/blogs/image-20221229154248800.png" alt="image-20221229154248800"></p>
<p>参考实现</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">DoublyLinkedListSentinel</span> <span class="keyword">implements</span> <span class="title class_">Iterable</span>&lt;Integer&gt; &#123;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> Iterator&lt;Integer&gt; <span class="title function_">iterator</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">new</span> <span class="title class_">Iterator</span>&lt;&gt;() &#123;</span><br><span class="line">            <span class="type">Node</span> <span class="variable">p</span> <span class="operator">=</span> sentinel.next;</span><br><span class="line"></span><br><span class="line">            <span class="meta">@Override</span></span><br><span class="line">            <span class="keyword">public</span> <span class="type">boolean</span> <span class="title function_">hasNext</span><span class="params">()</span> &#123;</span><br><span class="line">                <span class="keyword">return</span> p != sentinel;</span><br><span class="line">            &#125;</span><br><span class="line"></span><br><span class="line">            <span class="meta">@Override</span></span><br><span class="line">            <span class="keyword">public</span> Integer <span class="title function_">next</span><span class="params">()</span> &#123;</span><br><span class="line">                <span class="type">int</span> <span class="variable">value</span> <span class="operator">=</span> p.value;</span><br><span class="line">                p = p.next;</span><br><span class="line">                <span class="keyword">return</span> value;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">static</span> <span class="keyword">class</span> <span class="title class_">Node</span> &#123;</span><br><span class="line">        Node prev;</span><br><span class="line">        <span class="type">int</span> value;</span><br><span class="line">        Node next;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">public</span> <span class="title function_">Node</span><span class="params">(Node prev, <span class="type">int</span> value, Node next)</span> &#123;</span><br><span class="line">            <span class="built_in">this</span>.prev = prev;</span><br><span class="line">            <span class="built_in">this</span>.value = value;</span><br><span class="line">            <span class="built_in">this</span>.next = next;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">final</span> <span class="type">Node</span> <span class="variable">sentinel</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Node</span>(<span class="literal">null</span>, -<span class="number">1</span>, <span class="literal">null</span>); <span class="comment">// 哨兵</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="title function_">DoublyLinkedListSentinel</span><span class="params">()</span> &#123;</span><br><span class="line">        sentinel.next = sentinel;</span><br><span class="line">        sentinel.prev = sentinel;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 添加到第一个</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> value 待添加值</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">addFirst</span><span class="params">(<span class="type">int</span> value)</span> &#123;</span><br><span class="line">        <span class="type">Node</span> <span class="variable">next</span> <span class="operator">=</span> sentinel.next;</span><br><span class="line">        <span class="type">Node</span> <span class="variable">prev</span> <span class="operator">=</span> sentinel;</span><br><span class="line">        <span class="type">Node</span> <span class="variable">added</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Node</span>(prev, value, next);</span><br><span class="line">        prev.next = added;</span><br><span class="line">        next.prev = added;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 添加到最后一个</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> value 待添加值</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">addLast</span><span class="params">(<span class="type">int</span> value)</span> &#123;</span><br><span class="line">        <span class="type">Node</span> <span class="variable">prev</span> <span class="operator">=</span> sentinel.prev;</span><br><span class="line">        <span class="type">Node</span> <span class="variable">next</span> <span class="operator">=</span> sentinel;</span><br><span class="line">        <span class="type">Node</span> <span class="variable">added</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Node</span>(prev, value, next);</span><br><span class="line">        prev.next = added;</span><br><span class="line">        next.prev = added;</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 删除第一个</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">removeFirst</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="type">Node</span> <span class="variable">removed</span> <span class="operator">=</span> sentinel.next;</span><br><span class="line">        <span class="keyword">if</span> (removed == sentinel) &#123;</span><br><span class="line">            <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">IllegalArgumentException</span>(<span class="string">&quot;非法&quot;</span>);</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="type">Node</span> <span class="variable">a</span> <span class="operator">=</span> sentinel;</span><br><span class="line">        <span class="type">Node</span> <span class="variable">b</span> <span class="operator">=</span> removed.next;</span><br><span class="line">        a.next = b;</span><br><span class="line">        b.prev = a;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 删除最后一个</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">removeLast</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="type">Node</span> <span class="variable">removed</span> <span class="operator">=</span> sentinel.prev;</span><br><span class="line">        <span class="keyword">if</span> (removed == sentinel) &#123;</span><br><span class="line">            <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">IllegalArgumentException</span>(<span class="string">&quot;非法&quot;</span>);</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="type">Node</span> <span class="variable">a</span> <span class="operator">=</span> removed.prev;</span><br><span class="line">        <span class="type">Node</span> <span class="variable">b</span> <span class="operator">=</span> sentinel;</span><br><span class="line">        a.next = b;</span><br><span class="line">        b.prev = a;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 根据值删除节点</span></span><br><span class="line"><span class="comment">     * &lt;p&gt;假定 value 在链表中作为 key, 有唯一性&lt;/p&gt;</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> value 待删除值</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">removeByValue</span><span class="params">(<span class="type">int</span> value)</span> &#123;</span><br><span class="line">        <span class="type">Node</span> <span class="variable">removed</span> <span class="operator">=</span> findNodeByValue(value);</span><br><span class="line">        <span class="keyword">if</span> (removed != <span class="literal">null</span>) &#123;</span><br><span class="line">            <span class="type">Node</span> <span class="variable">prev</span> <span class="operator">=</span> removed.prev;</span><br><span class="line">            <span class="type">Node</span> <span class="variable">next</span> <span class="operator">=</span> removed.next;</span><br><span class="line">            prev.next = next;</span><br><span class="line">            next.prev = prev;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> Node <span class="title function_">findNodeByValue</span><span class="params">(<span class="type">int</span> value)</span> &#123;</span><br><span class="line">        <span class="type">Node</span> <span class="variable">p</span> <span class="operator">=</span> sentinel.next;</span><br><span class="line">        <span class="keyword">while</span> (p != sentinel) &#123;</span><br><span class="line">            <span class="keyword">if</span> (p.value == value) &#123;</span><br><span class="line">                <span class="keyword">return</span> p;</span><br><span class="line">            &#125;</span><br><span class="line">            p = p.next;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> <span class="literal">null</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>



<h2 id="2-3-递归"><a href="#2-3-递归" class="headerlink" title="2.3 递归"></a>2.3 递归</h2><h3 id="概述-2"><a href="#概述-2" class="headerlink" title="概述"></a>概述</h3><p><strong>定义</strong></p>
<p>计算机科学中，递归是一种解决计算问题的方法，其中解决方案取决于同一类问题的更小子集</p>
<blockquote>
<p>In computer science, <strong>recursion</strong> is a method of solving a computational problem where the solution depends on solutions to smaller instances of the same problem.</p>
</blockquote>
<p>比如单链表递归遍历的例子：</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">void</span> <span class="title function_">f</span><span class="params">(Node node)</span> &#123;</span><br><span class="line">    <span class="keyword">if</span>(node == <span class="literal">null</span>) &#123;</span><br><span class="line">        <span class="keyword">return</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    println(<span class="string">&quot;before:&quot;</span> + node.value)</span><br><span class="line">    f(node.next);</span><br><span class="line">    println(<span class="string">&quot;after:&quot;</span> + node.value)</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>说明：</p>
<ol>
<li>自己调用自己，如果说每个函数对应着一种解决方案，自己调用自己意味着解决方案是一样的（有规律的）</li>
<li>每次调用，函数处理的数据会较上次缩减（子集），而且最后会缩减至无需继续递归</li>
<li>内层函数调用（子集处理）完成，外层函数才能算调用完成</li>
</ol>
<p><strong>原理</strong></p>
<p>假设链表中有 3 个节点，value 分别为 1，2，3，以上代码的执行流程就类似于下面的<strong>伪码</strong></p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="comment">// 1 -&gt; 2 -&gt; 3 -&gt; null  f(1)</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">void</span> <span class="title function_">f</span><span class="params">(Node node = <span class="number">1</span>)</span> &#123;</span><br><span class="line">    println(<span class="string">&quot;before:&quot;</span> + node.value) <span class="comment">// 1</span></span><br><span class="line">    <span class="keyword">void</span> <span class="title function_">f</span><span class="params">(Node node = <span class="number">2</span>)</span> &#123;</span><br><span class="line">        println(<span class="string">&quot;before:&quot;</span> + node.value) <span class="comment">// 2</span></span><br><span class="line">        <span class="keyword">void</span> <span class="title function_">f</span><span class="params">(Node node = <span class="number">3</span>)</span> &#123;</span><br><span class="line">            println(<span class="string">&quot;before:&quot;</span> + node.value) <span class="comment">// 3</span></span><br><span class="line">            <span class="keyword">void</span> <span class="title function_">f</span><span class="params">(Node node = <span class="literal">null</span>)</span> &#123;</span><br><span class="line">                <span class="keyword">if</span>(node == <span class="literal">null</span>) &#123;</span><br><span class="line">                    <span class="keyword">return</span>;</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line">            println(<span class="string">&quot;after:&quot;</span> + node.value) <span class="comment">// 3</span></span><br><span class="line">        &#125;</span><br><span class="line">        println(<span class="string">&quot;after:&quot;</span> + node.value) <span class="comment">// 2</span></span><br><span class="line">    &#125;</span><br><span class="line">    println(<span class="string">&quot;after:&quot;</span> + node.value) <span class="comment">// 1</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>



<p><strong>思路</strong></p>
<ol>
<li>确定能否使用递归求解</li>
<li>推导出递推关系，即父问题与子问题的关系，以及递归的结束条件</li>
</ol>
<p>例如之前遍历链表的递推关系为<br>$$<br>f(n) &#x3D;<br>\begin{cases}<br>停止&amp; n &#x3D; null \<br>f(n.next) &amp; n \neq null<br>\end{cases}<br>$$</p>
<ul>
<li>深入到最里层叫做<strong>递</strong></li>
<li>从最里层出来叫做<strong>归</strong></li>
<li>在<strong>递</strong>的过程中，外层函数内的局部变量（以及方法参数）并未消失，<strong>归</strong>的时候还可以用到</li>
</ul>
<h3 id="单路递归-Single-Recursion"><a href="#单路递归-Single-Recursion" class="headerlink" title="单路递归 Single Recursion"></a>单路递归 Single Recursion</h3><p><strong>E01. 阶乘</strong></p>
<p>用递归方法求阶乘 </p>
<ul>
<li><p>阶乘的定义 $n!&#x3D; 1⋅2⋅3⋯(n-2)⋅(n-1)⋅n$，其中 $n$ 为自然数，当然 $0! &#x3D; 1$</p>
</li>
<li><p>递推关系</p>
</li>
</ul>
<p>$$<br>f(n) &#x3D;<br>\begin{cases}<br>1 &amp; n &#x3D; 1\<br>n * f(n-1) &amp; n &gt; 1<br>\end{cases}<br>$$</p>
<p>代码</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">private</span> <span class="keyword">static</span> <span class="type">int</span> <span class="title function_">f</span><span class="params">(<span class="type">int</span> n)</span> &#123;</span><br><span class="line">    <span class="keyword">if</span> (n == <span class="number">1</span>) &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="number">1</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> n * f(n - <span class="number">1</span>);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>拆解<strong>伪码</strong>如下，假设 n 初始值为 3</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line">f(<span class="type">int</span> <span class="variable">n</span> <span class="operator">=</span> <span class="number">3</span>) &#123; <span class="comment">// 解决不了,递</span></span><br><span class="line">    <span class="keyword">return</span> <span class="number">3</span> * f(<span class="type">int</span> <span class="variable">n</span> <span class="operator">=</span> <span class="number">2</span>) &#123; <span class="comment">// 解决不了,继续递</span></span><br><span class="line">        <span class="keyword">return</span> <span class="number">2</span> * f(<span class="type">int</span> <span class="variable">n</span> <span class="operator">=</span> <span class="number">1</span>) &#123;</span><br><span class="line">            <span class="keyword">if</span> (n == <span class="number">1</span>) &#123; <span class="comment">// 可以解决, 开始归</span></span><br><span class="line">                <span class="keyword">return</span> <span class="number">1</span>;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>



<p><strong>E02. 反向打印字符串</strong></p>
<p>用递归反向打印字符串，n 为字符在整个字符串 str 中的索引位置</p>
<ul>
<li><strong>递</strong>：n 从 0 开始，每次 n + 1，一直递到 n &#x3D;&#x3D; str.length() - 1</li>
<li><strong>归</strong>：从 n &#x3D;&#x3D; str.length() 开始归，从归打印，自然是逆序的</li>
</ul>
<p>递推关系<br>$$<br>f(n) &#x3D;<br>\begin{cases}<br>停止 &amp; n &#x3D; str.length() \<br>f(n+1) &amp; 0 \leq n \leq str.length() - 1<br>\end{cases}<br>$$<br>代码为</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">reversePrint</span><span class="params">(String str, <span class="type">int</span> index)</span> &#123;</span><br><span class="line">    <span class="keyword">if</span> (index == str.length()) &#123;</span><br><span class="line">        <span class="keyword">return</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    reversePrint(str, index + <span class="number">1</span>);</span><br><span class="line">    System.out.println(str.charAt(index));</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>拆解<strong>伪码</strong>如下，假设字符串为 “abc”</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">void</span> <span class="title function_">reversePrint</span><span class="params">(String str, <span class="type">int</span> index = <span class="number">0</span>)</span> &#123;</span><br><span class="line">    <span class="keyword">void</span> <span class="title function_">reversePrint</span><span class="params">(String str, <span class="type">int</span> index = <span class="number">1</span>)</span> &#123;</span><br><span class="line">        <span class="keyword">void</span> <span class="title function_">reversePrint</span><span class="params">(String str, <span class="type">int</span> index = <span class="number">2</span>)</span> &#123;</span><br><span class="line">            <span class="keyword">void</span> <span class="title function_">reversePrint</span><span class="params">(String str, <span class="type">int</span> index = <span class="number">3</span>)</span> &#123; </span><br><span class="line">                <span class="keyword">if</span> (index == str.length()) &#123;</span><br><span class="line">                    <span class="keyword">return</span>; <span class="comment">// 开始归</span></span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line">            System.out.println(str.charAt(index)); <span class="comment">// 打印 c</span></span><br><span class="line">        &#125;</span><br><span class="line">        System.out.println(str.charAt(index)); <span class="comment">// 打印 b</span></span><br><span class="line">    &#125;</span><br><span class="line">    System.out.println(str.charAt(index)); <span class="comment">// 打印 a</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>



<h3 id="多路递归-Multi-Recursion"><a href="#多路递归-Multi-Recursion" class="headerlink" title="多路递归 Multi Recursion"></a>多路递归 Multi Recursion</h3><p><strong>E01. 斐波那契数列</strong></p>
<ul>
<li>之前的例子是每个递归函数只包含一个自身的调用，这称之为 single recursion</li>
<li>如果每个递归函数例包含多个自身调用，称之为 multi recursion</li>
</ul>
<p><strong>递推关系</strong><br>$$<br>f(n) &#x3D;<br>\begin{cases}<br>0 &amp; n&#x3D;0 \<br>1 &amp; n&#x3D;1 \<br>f(n-1) + f(n-2) &amp; n&gt;1<br>\end{cases}<br>$$</p>
<p>下面的表格列出了数列的前几项</p>
<table>
<thead>
<tr>
<th><em>F</em>0</th>
<th><em>F</em>1</th>
<th><em>F</em>2</th>
<th><em>F</em>3</th>
<th><em>F</em>4</th>
<th><em>F</em>5</th>
<th><em>F</em>6</th>
<th><em>F</em>7</th>
<th><em>F</em>8</th>
<th><em>F</em>9</th>
<th><em>F</em>10</th>
<th><em>F</em>11</th>
<th><em>F</em>12</th>
<th><em>F</em>13</th>
</tr>
</thead>
<tbody><tr>
<td>0</td>
<td>1</td>
<td>1</td>
<td>2</td>
<td>3</td>
<td>5</td>
<td>8</td>
<td>13</td>
<td>21</td>
<td>34</td>
<td>55</td>
<td>89</td>
<td>144</td>
<td>233</td>
</tr>
</tbody></table>
<p><strong>实现</strong></p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">static</span> <span class="type">int</span> <span class="title function_">f</span><span class="params">(<span class="type">int</span> n)</span> &#123;</span><br><span class="line">    <span class="keyword">if</span> (n == <span class="number">0</span>) &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">if</span> (n == <span class="number">1</span>) &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="number">1</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> f(n - <span class="number">1</span>) + f(n - <span class="number">2</span>);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p><strong>执行流程</strong></p>
<p><img src= "" data-lazy-src="/./imgs/2.gif"></p>
<ul>
<li>绿色代表正在执行（对应递），灰色代表执行结束（对应归）</li>
<li>递不到头，不能归，对应着深度优先搜索</li>
</ul>
<p><strong>时间复杂度</strong></p>
<ul>
<li>递归的次数也符合斐波那契规律，$2 * f(n+1)-1$</li>
<li>时间复杂度推导过程<ul>
<li>斐波那契通项公式 $f(n) &#x3D; \frac{1}{\sqrt{5}}*({\frac{1+\sqrt{5}}{2}}^n - {\frac{1-\sqrt{5}}{2}}^n)$</li>
<li>简化为：$f(n) &#x3D; \frac{1}{2.236}*({1.618}^n - {(-0.618)}^n)$</li>
<li>带入递归次数公式 $2<em>\frac{1}{2.236}</em>({1.618}^{n+1} - {(-0.618)}^{n+1})-1$</li>
<li>时间复杂度为 $\Theta(1.618^n)$</li>
</ul>
</li>
</ul>
<blockquote>
<ol>
<li>更多 Fibonacci 参考[^8][^9][^10]</li>
<li>以上时间复杂度分析，未考虑大数相加的因素</li>
</ol>
</blockquote>
<p><strong>变体1 - 兔子问题[^8]</strong></p>
<p><img src= "" data-lazy-src="http://rx6zk4j2b.hn-bkt.clouddn.com/blogs/image-20221110155655827.png" alt="image-20221110155655827"></p>
<ul>
<li>第一个月，有一对未成熟的兔子（黑色，注意图中个头较小）</li>
<li>第二个月，它们成熟</li>
<li>第三个月，它们能产下一对新的小兔子（蓝色）</li>
<li>所有兔子遵循相同规律，求第 $n$ 个月的兔子数</li>
</ul>
<p><strong>分析</strong></p>
<p>兔子问题如何与斐波那契联系起来呢？设第 n 个月兔子数为 $f(n)$</p>
<ul>
<li>$f(n)$ &#x3D; 上个月兔子数 + 新生的小兔子数</li>
<li>而【新生的小兔子数】实际就是【上个月成熟的兔子数】</li>
<li>因为需要一个月兔子就成熟，所以【上个月成熟的兔子数】也就是【上上个月的兔子数】</li>
<li>上个月兔子数，即 $f(n-1)$</li>
<li>上上个月的兔子数，即 $f(n-2)$</li>
</ul>
<p>因此本质还是斐波那契数列，只是从其第一项开始</p>
<p><strong>变体2 - 青蛙爬楼梯</strong></p>
<ul>
<li>楼梯有 $n$ 阶</li>
<li>青蛙要爬到楼顶，可以一次跳一阶，也可以一次跳两阶</li>
<li>只能向上跳，问有多少种跳法</li>
</ul>
<p><strong>分析</strong></p>
<table>
<thead>
<tr>
<th>n</th>
<th>跳法</th>
<th>规律</th>
</tr>
</thead>
<tbody><tr>
<td>1</td>
<td>(1)</td>
<td>暂时看不出</td>
</tr>
<tr>
<td>2</td>
<td>(1,1)  (2)</td>
<td>暂时看不出</td>
</tr>
<tr>
<td>3</td>
<td>(1,1,1)  (1,2)  (2,1)</td>
<td>暂时看不出</td>
</tr>
<tr>
<td>4</td>
<td>(1,1,1,1)    (1,2,1)    (2,1,1)<br />(1,1,2)  (2,2)</td>
<td>最后一跳，跳一个台阶的，基于f(3)<br />最后一跳，跳两个台阶的，基于f(2)</td>
</tr>
<tr>
<td>5</td>
<td>…</td>
<td>…</td>
</tr>
</tbody></table>
<ul>
<li><p>因此本质上还是斐波那契数列，只是从其第二项开始</p>
</li>
<li><p>对应 leetcode 题目 <a target="_blank" rel="noopener" href="https://leetcode.cn/problems/climbing-stairs/">70. 爬楼梯 - 力扣（LeetCode）</a></p>
</li>
</ul>
<h3 id="递归优化-记忆法"><a href="#递归优化-记忆法" class="headerlink" title="递归优化-记忆法"></a>递归优化-记忆法</h3><p>上述代码存在很多重复的计算，例如求 $f(5)$ 递归分解过程</p>
<p><img src= "" data-lazy-src="http://rx6zk4j2b.hn-bkt.clouddn.com/blogs/image-20221207092417933.png" alt="image-20221207092417933"></p>
<p>可以看到（颜色相同的是重复的）：</p>
<ul>
<li>$f(3)$ 重复了 2 次</li>
<li>$f(2)$ 重复了 3 次</li>
<li>$f(1)$ 重复了 5 次</li>
<li>$f(0)$ 重复了 3 次</li>
</ul>
<p>随着  $n$ 的增大，重复次数非常可观，如何优化呢？</p>
<p><strong>Memoization</strong> 记忆法（也称备忘录）是一种优化技术，通过存储函数调用结果（通常比较昂贵），当再次出现相同的输入（子问题）时，就能实现加速效果，改进后的代码</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> &#123;</span><br><span class="line">    <span class="type">int</span> <span class="variable">n</span> <span class="operator">=</span> <span class="number">13</span>;</span><br><span class="line">    <span class="type">int</span>[] cache = <span class="keyword">new</span> <span class="title class_">int</span>[n + <span class="number">1</span>];</span><br><span class="line">    Arrays.fill(cache, -<span class="number">1</span>);</span><br><span class="line">    cache[<span class="number">0</span>] = <span class="number">0</span>;</span><br><span class="line">    cache[<span class="number">1</span>] = <span class="number">1</span>;</span><br><span class="line">    System.out.println(f(cache, n));</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">static</span> <span class="type">int</span> <span class="title function_">f</span><span class="params">(<span class="type">int</span>[] cache, <span class="type">int</span> n)</span> &#123;</span><br><span class="line">    <span class="keyword">if</span> (cache[n] != -<span class="number">1</span>) &#123;</span><br><span class="line">        <span class="keyword">return</span> cache[n];</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    cache[n] = f(cache, n - <span class="number">1</span>) + f(cache, n - <span class="number">2</span>);</span><br><span class="line">    <span class="keyword">return</span> cache[n];</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>优化后的图示，只要结果被缓存，就<strong>不会执行其子问题</strong></p>
<p><img src= "" data-lazy-src="http://rx6zk4j2b.hn-bkt.clouddn.com/blogs/image-20221213173225807.png" alt="image-20221213173225807"></p>
<ul>
<li>改进后的时间复杂度为 $O(n)$</li>
<li>请自行验证改进后的效果</li>
<li>请自行分析改进后的空间复杂度</li>
</ul>
<blockquote>
<p><strong>注意</strong></p>
<ol>
<li>记忆法是动态规划的一种情况，强调的是自顶向下的解决</li>
<li>记忆法的本质是空间换时间</li>
</ol>
</blockquote>
<h3 id="递归优化-尾递归"><a href="#递归优化-尾递归" class="headerlink" title="递归优化-尾递归"></a>递归优化-尾递归</h3><p><strong>爆栈</strong></p>
<p>用递归做 $n + (n-1) + (n-2) … + 1$</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">static</span> <span class="type">long</span> <span class="title function_">sum</span><span class="params">(<span class="type">long</span> n)</span> &#123;</span><br><span class="line">    <span class="keyword">if</span> (n == <span class="number">1</span>) &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="number">1</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> n + sum(n - <span class="number">1</span>);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>在我的机器上  $n &#x3D; 12000$ 时，爆栈了</p>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">Exception in thread &quot;main&quot; java.lang.StackOverflowError</span><br><span class="line">	at Test.sum(Test.java:10)</span><br><span class="line">	at Test.sum(Test.java:10)</span><br><span class="line">	at Test.sum(Test.java:10)</span><br><span class="line">	at Test.sum(Test.java:10)</span><br><span class="line">	at Test.sum(Test.java:10)</span><br><span class="line">	...</span><br></pre></td></tr></table></figure>

<p>为什么呢？</p>
<ul>
<li>每次方法调用是需要消耗一定的栈内存的，这些内存用来存储方法参数、方法内局部变量、返回地址等等</li>
<li>方法调用占用的内存需要等到<strong>方法结束时</strong>才会释放</li>
<li>而递归调用我们之前讲过，不到最深不会回头，最内层方法没完成之前，外层方法都结束不了<ul>
<li>例如，$sum(3)$ 这个方法内有个需要执行 $3 + sum(2)$，$sum(2)$ 没返回前，加号前面的 $3$ 不能释放 </li>
<li>看下面伪码</li>
</ul>
</li>
</ul>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="type">long</span> <span class="title function_">sum</span><span class="params">(<span class="type">long</span> n = <span class="number">3</span>)</span> &#123;</span><br><span class="line">    <span class="keyword">return</span> <span class="number">3</span> + <span class="type">long</span> <span class="title function_">sum</span><span class="params">(<span class="type">long</span> n = <span class="number">2</span>)</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="number">2</span> + <span class="type">long</span> <span class="title function_">sum</span><span class="params">(<span class="type">long</span> n = <span class="number">1</span>)</span> &#123;</span><br><span class="line">            <span class="keyword">return</span> <span class="number">1</span>;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>



<p><strong>尾调用</strong></p>
<p>如果函数的最后一步是调用一个函数，那么称为尾调用，例如</p>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="keyword">function</span> <span class="title function_">a</span>(<span class="params"></span>) &#123;</span><br><span class="line">    <span class="keyword">return</span> <span class="title function_">b</span>()</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>



<p>下面三段代码<strong>不能</strong>叫做尾调用</p>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="keyword">function</span> <span class="title function_">a</span>(<span class="params"></span>) &#123;</span><br><span class="line">    <span class="keyword">const</span> c = <span class="title function_">b</span>()</span><br><span class="line">    <span class="keyword">return</span> c</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<ul>
<li>因为最后一步并非调用函数</li>
</ul>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="keyword">function</span> <span class="title function_">a</span>(<span class="params"></span>) &#123;</span><br><span class="line">    <span class="keyword">return</span> <span class="title function_">b</span>() + <span class="number">1</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<ul>
<li>最后一步执行的是加法</li>
</ul>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="keyword">function</span> <span class="title function_">a</span>(<span class="params">x</span>) &#123;</span><br><span class="line">    <span class="keyword">return</span> <span class="title function_">b</span>() + x</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<ul>
<li>最后一步执行的是加法</li>
</ul>
<p><strong>一些语言</strong>[^11]的编译器能够对尾调用做优化，例如</p>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="keyword">function</span> <span class="title function_">a</span>(<span class="params"></span>) &#123;</span><br><span class="line">    <span class="comment">// 做前面的事</span></span><br><span class="line">    <span class="keyword">return</span> <span class="title function_">b</span>() </span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">function</span> <span class="title function_">b</span>(<span class="params"></span>) &#123;</span><br><span class="line">    <span class="comment">// 做前面的事</span></span><br><span class="line">    <span class="keyword">return</span> <span class="title function_">c</span>()</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">function</span> <span class="title function_">c</span>(<span class="params"></span>) &#123;</span><br><span class="line">    <span class="keyword">return</span> <span class="number">1000</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="title function_">a</span>()</span><br></pre></td></tr></table></figure>

<p>没优化之前的<strong>伪码</strong></p>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="keyword">function</span> <span class="title function_">a</span>(<span class="params"></span>) &#123;</span><br><span class="line">    <span class="keyword">return</span> <span class="keyword">function</span> <span class="title function_">b</span>(<span class="params"></span>) &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">function</span> <span class="title function_">c</span>(<span class="params"></span>) &#123;</span><br><span class="line">            <span class="keyword">return</span> <span class="number">1000</span></span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>优化后<strong>伪码</strong>如下</p>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="title function_">a</span>()</span><br><span class="line"><span class="title function_">b</span>()</span><br><span class="line"><span class="title function_">c</span>()</span><br></pre></td></tr></table></figure>

<p>为何尾递归才能优化？</p>
<p>调用 a 时</p>
<ul>
<li>a 返回时发现：没什么可留给 b 的，将来返回的结果 b 提供就可以了，用不着我 a 了，我的内存就可以释放</li>
</ul>
<p>调用 b 时</p>
<ul>
<li>b 返回时发现：没什么可留给 c 的，将来返回的结果 c 提供就可以了，用不着我 b 了，我的内存就可以释放</li>
</ul>
<p>如果调用 a 时</p>
<ul>
<li>不是尾调用，例如 return b() + 1，那么 a 就不能提前结束，因为它还得利用 b 的结果做加法</li>
</ul>
<p><strong>尾递归</strong></p>
<p>尾递归是尾调用的一种特例，也就是最后一步执行的是同一个函数</p>
<p><strong>尾递归避免爆栈</strong></p>
<p>安装 Scala</p>
<p><img src= "" data-lazy-src="http://rx6zk4j2b.hn-bkt.clouddn.com/blogs/image-20221111122709227.png" alt="image-20221111122709227"></p>
<p>Scala 入门</p>
<figure class="highlight scala"><table><tr><td class="code"><pre><span class="line"><span class="class"><span class="keyword">object</span> <span class="title">Main</span> </span>&#123;</span><br><span class="line">  <span class="function"><span class="keyword">def</span> <span class="title">main</span></span>(args: <span class="type">Array</span>[<span class="type">String</span>]): <span class="type">Unit</span> = &#123;</span><br><span class="line">    println(<span class="string">&quot;Hello Scala&quot;</span>)</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<ul>
<li>Scala 是 java 的近亲，java 中的类都可以拿来重用</li>
<li>类型是放在变量后面的</li>
<li>Unit 表示无返回值，类似于 void</li>
<li>不需要以分号作为结尾，当然加上也对</li>
</ul>
<p>还是先写一个会爆栈的函数</p>
<figure class="highlight scala"><table><tr><td class="code"><pre><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">sum</span></span>(n: <span class="type">Long</span>): <span class="type">Long</span> = &#123;</span><br><span class="line">    <span class="keyword">if</span> (n == <span class="number">1</span>) &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="number">1</span></span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> n + sum(n - <span class="number">1</span>)</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<ul>
<li>Scala 最后一行代码若作为返回值，可以省略 return</li>
</ul>
<p>不出所料，在 $n &#x3D; 11000$ 时，还是出了异常</p>
<figure class="highlight scala"><table><tr><td class="code"><pre><span class="line">println(sum(<span class="number">11000</span>))</span><br><span class="line"></span><br><span class="line"><span class="type">Exception</span> in thread <span class="string">&quot;main&quot;</span> java.lang.<span class="type">StackOverflowError</span></span><br><span class="line">	at <span class="type">Main</span>$.sum(<span class="type">Main</span>.scala:<span class="number">25</span>)</span><br><span class="line">	at <span class="type">Main</span>$.sum(<span class="type">Main</span>.scala:<span class="number">25</span>)</span><br><span class="line">	at <span class="type">Main</span>$.sum(<span class="type">Main</span>.scala:<span class="number">25</span>)</span><br><span class="line">	at <span class="type">Main</span>$.sum(<span class="type">Main</span>.scala:<span class="number">25</span>)</span><br><span class="line">	...</span><br></pre></td></tr></table></figure>



<p>这是因为以上代码，还不是尾调用，要想成为尾调用，那么：</p>
<ol>
<li>最后一行代码，必须是一次函数调用</li>
<li>内层函数必须<strong>摆脱</strong>与外层函数的关系，内层函数<strong>执行后</strong>不依赖于外层的变量或常量</li>
</ol>
<figure class="highlight scala"><table><tr><td class="code"><pre><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">sum</span></span>(n: <span class="type">Long</span>): <span class="type">Long</span> = &#123;</span><br><span class="line">    <span class="keyword">if</span> (n == <span class="number">1</span>) &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="number">1</span></span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> n + sum(n - <span class="number">1</span>)  <span class="comment">// 依赖于外层函数的 n 变量</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>如何让它执行后就摆脱对 n 的依赖呢？</p>
<ul>
<li>不能等递归回来再做加法，那样就必须保留外层的 n</li>
<li>把 n 当做内层函数的一个参数传进去，这时 n 就属于内层函数了</li>
<li>传参时就完成累加, 不必等回来时累加</li>
</ul>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">sum(n - 1, n + 累加器)</span><br></pre></td></tr></table></figure>



<p>改写后代码如下</p>
<figure class="highlight scala"><table><tr><td class="code"><pre><span class="line"><span class="meta">@tailrec</span></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">sum</span></span>(n: <span class="type">Long</span>, accumulator: <span class="type">Long</span>): <span class="type">Long</span> = &#123;</span><br><span class="line">    <span class="keyword">if</span> (n == <span class="number">1</span>) &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="number">1</span> + accumulator</span><br><span class="line">    &#125; </span><br><span class="line">    <span class="keyword">return</span> sum(n - <span class="number">1</span>, n + accumulator)</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<ul>
<li>accumulator 作为累加器</li>
<li>@tailrec 注解是 scala 提供的，用来检查方法是否符合尾递归</li>
<li>这回 sum(10000000, 0) 也没有问题，打印 50000005000000</li>
</ul>
<p>执行流程如下，以<strong>伪码</strong>表示 $sum(4, 0)$</p>
<figure class="highlight scala"><table><tr><td class="code"><pre><span class="line"><span class="comment">// 首次调用</span></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">sum</span></span>(n = <span class="number">4</span>, accumulator = <span class="number">0</span>): <span class="type">Long</span> = &#123;</span><br><span class="line">    <span class="keyword">return</span> sum(<span class="number">4</span> - <span class="number">1</span>, <span class="number">4</span> + accumulator)</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 接下来调用内层 sum, 传参时就完成了累加, 不必等回来时累加，当内层 sum 调用后，外层 sum 空间没必要保留</span></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">sum</span></span>(n = <span class="number">3</span>, accumulator = <span class="number">4</span>): <span class="type">Long</span> = &#123;</span><br><span class="line">    <span class="keyword">return</span> sum(<span class="number">3</span> - <span class="number">1</span>, <span class="number">3</span> + accumulator)</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 继续调用内层 sum</span></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">sum</span></span>(n = <span class="number">2</span>, accumulator = <span class="number">7</span>): <span class="type">Long</span> = &#123;</span><br><span class="line">    <span class="keyword">return</span> sum(<span class="number">2</span> - <span class="number">1</span>, <span class="number">2</span> + accumulator)</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 继续调用内层 sum, 这是最后的 sum 调用完就返回最后结果 10, 前面所有其它 sum 的空间早已释放</span></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">sum</span></span>(n = <span class="number">1</span>, accumulator = <span class="number">9</span>): <span class="type">Long</span> = &#123;</span><br><span class="line">    <span class="keyword">if</span> (<span class="number">1</span> == <span class="number">1</span>) &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="number">1</span> + accumulator</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>本质上，尾递归优化是将函数的<strong>递归</strong>调用，变成了函数的<strong>循环</strong>调用</p>
<p><strong>改循环避免爆栈</strong></p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> &#123;</span><br><span class="line">    <span class="type">long</span> <span class="variable">n</span> <span class="operator">=</span> <span class="number">100000000</span>;</span><br><span class="line">    <span class="type">long</span> <span class="variable">sum</span> <span class="operator">=</span> <span class="number">0</span>;</span><br><span class="line">    <span class="keyword">for</span> (<span class="type">long</span> <span class="variable">i</span> <span class="operator">=</span> n; i &gt;= <span class="number">1</span>; i--) &#123;</span><br><span class="line">        sum += i;</span><br><span class="line">    &#125;</span><br><span class="line">    System.out.println(sum);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>



<h3 id="递归时间复杂度-Master-theorem-14"><a href="#递归时间复杂度-Master-theorem-14" class="headerlink" title="递归时间复杂度-Master theorem[^14]"></a>递归时间复杂度-Master theorem[^14]</h3><p>若有递归式<br>$$<br>T(n) &#x3D; aT(\frac{n}{b}) + f(n)<br>$$<br>其中 </p>
<ul>
<li>$T(n)$ 是问题的运行时间，$n$ 是数据规模</li>
<li>$a$ 是子问题个数</li>
<li>$T(\frac{n}{b})$ 是子问题运行时间，每个子问题被拆成原问题数据规模的 $\frac{n}{b}$</li>
<li>$f(n)$ 是除递归外执行的计算</li>
</ul>
<p>令 $x &#x3D; \log_{b}{a}$，即 $x &#x3D; \log_{子问题缩小倍数}{子问题个数}$</p>
<p>那么<br>$$<br>T(n) &#x3D;<br>\begin{cases}<br>\Theta(n^x) &amp; f(n) &#x3D; O(n^c) 并且 c \lt x\<br>\Theta(n^x\log{n}) &amp; f(n) &#x3D; \Theta(n^x)\<br>\Theta(n^c) &amp; f(n) &#x3D; \Omega(n^c) 并且 c \gt x<br>\end{cases}<br>$$</p>
<p><strong>例1</strong></p>
<p>$T(n) &#x3D; 2T(\frac{n}{2}) + n^4$ </p>
<ul>
<li>此时 $x &#x3D; 1 &lt; 4$，由后者决定整个时间复杂度 $\Theta(n^4)$</li>
<li>如果觉得对数不好算，可以换为求【$b$ 的几次方能等于 $a$】</li>
</ul>
<p><strong>例2</strong></p>
<p>$T(n) &#x3D; T(\frac{7n}{10}) + n$</p>
<ul>
<li>$a&#x3D;1, b&#x3D;\frac{10}{7}, x&#x3D;0, c&#x3D;1$</li>
<li>此时 $x &#x3D; 0 &lt; 1$，由后者决定整个时间复杂度 $\Theta(n)$</li>
</ul>
<p><strong>例3</strong></p>
<p>$T(n) &#x3D; 16T(\frac{n}{4}) + n^2$</p>
<ul>
<li>$a&#x3D;16, b&#x3D;4, x&#x3D;2, c&#x3D;2$</li>
<li>此时 $x&#x3D;2 &#x3D; c$，时间复杂度 $\Theta(n^2 \log{n})$</li>
</ul>
<p><strong>例4</strong></p>
<p>$T(n)&#x3D;7T(\frac{n}{3}) + n^2$</p>
<ul>
<li>$a&#x3D;7, b&#x3D;3, x&#x3D;1.?, c&#x3D;2$</li>
<li>此时 $x &#x3D; \log_{3}{7} &lt; 2$，由后者决定整个时间复杂度 $\Theta(n^2)$</li>
</ul>
<p><strong>例5</strong></p>
<p>$T(n) &#x3D; 7T(\frac{n}{2}) + n^2$</p>
<ul>
<li>$a&#x3D;7, b&#x3D;2, x&#x3D;2.?, c&#x3D;2$</li>
<li>此时 $x &#x3D; log_2{7} &gt; 2$，由前者决定整个时间复杂度 $\Theta(n^{\log_2{7}})$</li>
</ul>
<p><strong>例6</strong></p>
<p>$T(n) &#x3D; 2T(\frac{n}{4}) + \sqrt{n}$</p>
<ul>
<li>$a&#x3D;2, b&#x3D;4, x &#x3D; 0.5, c&#x3D;0.5$</li>
<li>此时 $x &#x3D; 0.5 &#x3D; c$，时间复杂度 $\Theta(\sqrt{n}\ \log{n})$</li>
</ul>
<p><strong>例7. 二分查找递归</strong></p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="type">int</span> <span class="title function_">f</span><span class="params">(<span class="type">int</span>[] a, <span class="type">int</span> target, <span class="type">int</span> i, <span class="type">int</span> j)</span> &#123;</span><br><span class="line">    <span class="keyword">if</span> (i &gt; j) &#123;</span><br><span class="line">        <span class="keyword">return</span> -<span class="number">1</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="type">int</span> <span class="variable">m</span> <span class="operator">=</span> (i + j) &gt;&gt;&gt; <span class="number">1</span>;</span><br><span class="line">    <span class="keyword">if</span> (target &lt; a[m]) &#123;</span><br><span class="line">        <span class="keyword">return</span> f(a, target, i, m - <span class="number">1</span>);</span><br><span class="line">    &#125; <span class="keyword">else</span> <span class="keyword">if</span> (a[m] &lt; target) &#123;</span><br><span class="line">        <span class="keyword">return</span> f(a, target, m + <span class="number">1</span>, j);</span><br><span class="line">    &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> m;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<ul>
<li>子问题个数 $a &#x3D; 1$</li>
<li>子问题数据规模缩小倍数 $b &#x3D; 2$</li>
<li>除递归外执行的计算是常数级 $c&#x3D;0$</li>
</ul>
<p>$T(n) &#x3D; T(\frac{n}{2}) + n^0$</p>
<ul>
<li>此时 $x&#x3D;0 &#x3D; c$，时间复杂度 $\Theta(\log{n})$</li>
</ul>
<p><strong>例8. 归并排序递归</strong></p>
<figure class="highlight python"><table><tr><td class="code"><pre><span class="line">void split(B[], i, j, A[])</span><br><span class="line">&#123;</span><br><span class="line">    <span class="keyword">if</span> (j - i &lt;= <span class="number">1</span>)                    </span><br><span class="line">        <span class="keyword">return</span>;                                </span><br><span class="line">    m = (i + j) / <span class="number">2</span>;             </span><br><span class="line">    </span><br><span class="line">    // 递归</span><br><span class="line">    split(A, i, m, B);  </span><br><span class="line">    split(A, m, j, B); </span><br><span class="line">    </span><br><span class="line">    // 合并</span><br><span class="line">    merge(B, i, m, j, A);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<ul>
<li>子问题个数 $a&#x3D;2$</li>
<li>子问题数据规模缩小倍数 $b&#x3D;2$</li>
<li>除递归外，主要时间花在合并上，它可以用 $f(n) &#x3D; n$ 表示</li>
</ul>
<p>$T(n) &#x3D; 2T(\frac{n}{2}) + n$</p>
<ul>
<li>此时 $x&#x3D;1&#x3D;c$，时间复杂度 $\Theta(n\log{n})$</li>
</ul>
<p><strong>例9. 快速排序递归</strong></p>
<figure class="highlight python"><table><tr><td class="code"><pre><span class="line">algorithm quicksort(A, lo, hi) <span class="keyword">is</span> </span><br><span class="line">  <span class="keyword">if</span> lo &gt;= hi || lo &lt; <span class="number">0</span> then </span><br><span class="line">    <span class="keyword">return</span></span><br><span class="line">  </span><br><span class="line">  // 分区</span><br><span class="line">  p := partition(A, lo, hi) </span><br><span class="line">  </span><br><span class="line">  // 递归</span><br><span class="line">  quicksort(A, lo, p - <span class="number">1</span>) </span><br><span class="line">  quicksort(A, p + <span class="number">1</span>, hi) </span><br></pre></td></tr></table></figure>

<ul>
<li>子问题个数 $a&#x3D;2$</li>
<li>子问题数据规模缩小倍数<ul>
<li>如果分区分的好，$b&#x3D;2$</li>
<li>如果分区没分好，例如分区1 的数据是 0，分区 2 的数据是 $n-1$</li>
</ul>
</li>
<li>除递归外，主要时间花在分区上，它可以用 $f(n) &#x3D; n$ 表示</li>
</ul>
<p>情况1 - 分区分的好</p>
<p>$T(n) &#x3D; 2T(\frac{n}{2}) + n$</p>
<ul>
<li>此时 $x&#x3D;1&#x3D;c$，时间复杂度 $\Theta(n\log{n})$</li>
</ul>
<p>情况2 - 分区没分好</p>
<p>$T(n) &#x3D; T(n-1) + T(1) + n$</p>
<ul>
<li>此时不能用主定理求解</li>
</ul>
<h3 id="递归时间复杂度-展开求解"><a href="#递归时间复杂度-展开求解" class="headerlink" title="递归时间复杂度-展开求解"></a>递归时间复杂度-展开求解</h3><p>像下面的递归式，都不能用主定理求解</p>
<p><strong>例1 - 递归求和</strong></p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="type">long</span> <span class="title function_">sum</span><span class="params">(<span class="type">long</span> n)</span> &#123;</span><br><span class="line">    <span class="keyword">if</span> (n == <span class="number">1</span>) &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="number">1</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> n + sum(n - <span class="number">1</span>);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>$T(n) &#x3D; T(n-1) + c$，$T(1) &#x3D; c$</p>
<p>下面为展开过程</p>
<p>$T(n) &#x3D; T(n-2) + c + c$</p>
<p>$T(n) &#x3D; T(n-3) + c + c + c$</p>
<p>…</p>
<p>$T(n) &#x3D; T(n-(n-1)) + (n-1)c$ </p>
<ul>
<li>其中 $T(n-(n-1))$ 即 $T(1)$</li>
<li>带入求得 $T(n) &#x3D; c + (n-1)c &#x3D; nc$</li>
</ul>
<p>时间复杂度为 $O(n)$</p>
<p><strong>例2 - 递归冒泡排序</strong></p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">void</span> <span class="title function_">bubble</span><span class="params">(<span class="type">int</span>[] a, <span class="type">int</span> high)</span> &#123;</span><br><span class="line">    <span class="keyword">if</span>(<span class="number">0</span> == high) &#123;</span><br><span class="line">        <span class="keyword">return</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">for</span> (<span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> <span class="number">0</span>; i &lt; high; i++) &#123;</span><br><span class="line">        <span class="keyword">if</span> (a[i] &gt; a[i + <span class="number">1</span>]) &#123;</span><br><span class="line">            swap(a, i, i + <span class="number">1</span>);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    bubble(a, high - <span class="number">1</span>);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>$T(n) &#x3D; T(n-1) + n$，$T(1) &#x3D; c$</p>
<p>下面为展开过程</p>
<p>$T(n) &#x3D; T(n-2) + (n-1) + n$</p>
<p>$T(n) &#x3D; T(n-3) + (n-2) + (n-1) + n$</p>
<p>…</p>
<p>$T(n) &#x3D; T(1) + 2 + … + n &#x3D; T(1) + (n-1)\frac{2+n}{2} &#x3D; c + \frac{n^2}{2} + \frac{n}{2} -1$</p>
<p>时间复杂度 $O(n^2)$</p>
<blockquote>
<p>注：</p>
<ul>
<li>等差数列求和为 $个数*\frac{\vert首项-末项\vert}{2}$</li>
</ul>
</blockquote>
<p><strong>例3 - 递归快排</strong></p>
<p>快速排序分区没分好的极端情况</p>
<p>$T(n) &#x3D; T(n-1) + T(1) + n$，$T(1) &#x3D; c$</p>
<p>$T(n) &#x3D; T(n-1) + c + n$</p>
<p>下面为展开过程</p>
<p>$T(n) &#x3D; T(n-2) + c + (n-1) + c + n$</p>
<p>$T(n) &#x3D; T(n-3) + c + (n-2) + c + (n-1) + c + n$</p>
<p>…</p>
<p>$T(n) &#x3D; T(n-(n-1)) + (n-1)c + 2+…+n &#x3D; \frac{n^2}{2} + \frac{2cn+n}{2} -1$</p>
<p>时间复杂度 $O(n^2)$</p>
<p>不会推导的同学可以进入 <a target="_blank" rel="noopener" href="https://www.wolframalpha.com/">https://www.wolframalpha.com/</a></p>
<ul>
<li>例1 输入 f(n) &#x3D; f(n - 1) + c, f(1) &#x3D; c</li>
<li>例2 输入 f(n) &#x3D; f(n - 1) + n, f(1) &#x3D; c</li>
<li>例3 输入 f(n) &#x3D; f(n - 1) + n + c, f(1) &#x3D; c</li>
</ul>
<h2 id="2-4-队列"><a href="#2-4-队列" class="headerlink" title="2.4 队列"></a>2.4 队列</h2><h3 id="概述-3"><a href="#概述-3" class="headerlink" title="概述"></a>概述</h3><p>计算机科学中，queue 是以顺序的方式维护的一组数据集合，在一端添加数据，从另一端移除数据。习惯来说，添加的一端称为<strong>尾</strong>，移除的一端称为<strong>头</strong>，就如同生活中的排队买商品</p>
<blockquote>
<p>In computer science, a <strong>queue</strong> is a collection of entities that are maintained in a sequence and can be modified by  the addition of entities at one end of the sequence and the removal of  entities from the other end of the sequence</p>
</blockquote>
<p>先定义一个简化的队列接口</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">interface</span> <span class="title class_">Queue</span>&lt;E&gt; &#123;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 向队列尾插入值</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> value 待插入值</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@return</span> 插入成功返回 true, 插入失败返回 false</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="type">boolean</span> <span class="title function_">offer</span><span class="params">(E value)</span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 从对列头获取值, 并移除</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@return</span> 如果队列非空返回对头值, 否则返回 null</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    E <span class="title function_">poll</span><span class="params">()</span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 从对列头获取值, 不移除</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@return</span> 如果队列非空返回对头值, 否则返回 null</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    E <span class="title function_">peek</span><span class="params">()</span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 检查队列是否为空</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@return</span> 空返回 true, 否则返回 false</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="type">boolean</span> <span class="title function_">isEmpty</span><span class="params">()</span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 检查队列是否已满</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@return</span> 满返回 true, 否则返回 false</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="type">boolean</span> <span class="title function_">isFull</span><span class="params">()</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>



<h3 id="链表实现"><a href="#链表实现" class="headerlink" title="链表实现"></a>链表实现</h3><p>下面以<strong>单向环形带哨兵</strong>链表方式来实现队列</p>
<p><img src= "" data-lazy-src="http://rx6zk4j2b.hn-bkt.clouddn.com/blogs/image-20221230150105089.png" alt="image-20221230150105089"></p>
<p><img src= "" data-lazy-src="http://rx6zk4j2b.hn-bkt.clouddn.com/blogs/image-20221230150141318.png" alt="image-20221230150141318"></p>
<p><img src= "" data-lazy-src="http://rx6zk4j2b.hn-bkt.clouddn.com/blogs/image-20221230150153271.png" alt="image-20221230150153271"></p>
<p>代码</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">LinkedListQueue</span>&lt;E&gt;</span><br><span class="line">        <span class="keyword">implements</span> <span class="title class_">Queue</span>&lt;E&gt;, Iterable&lt;E&gt; &#123;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">class</span> <span class="title class_">Node</span>&lt;E&gt; &#123;</span><br><span class="line">        E value;</span><br><span class="line">        Node&lt;E&gt; next;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">public</span> <span class="title function_">Node</span><span class="params">(E value, Node&lt;E&gt; next)</span> &#123;</span><br><span class="line">            <span class="built_in">this</span>.value = value;</span><br><span class="line">            <span class="built_in">this</span>.next = next;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> Node&lt;E&gt; head = <span class="keyword">new</span> <span class="title class_">Node</span>&lt;&gt;(<span class="literal">null</span>, <span class="literal">null</span>);</span><br><span class="line">    <span class="keyword">private</span> Node&lt;E&gt; tail = head;</span><br><span class="line">    <span class="keyword">private</span> <span class="type">int</span> <span class="variable">size</span> <span class="operator">=</span> <span class="number">0</span>;</span><br><span class="line">    <span class="keyword">private</span> <span class="type">int</span> <span class="variable">capacity</span> <span class="operator">=</span> Integer.MAX_VALUE;</span><br><span class="line"></span><br><span class="line">    &#123;</span><br><span class="line">        tail.next = head;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="title function_">LinkedListQueue</span><span class="params">()</span> &#123;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="title function_">LinkedListQueue</span><span class="params">(<span class="type">int</span> capacity)</span> &#123;</span><br><span class="line">        <span class="built_in">this</span>.capacity = capacity;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> <span class="type">boolean</span> <span class="title function_">offer</span><span class="params">(E value)</span> &#123;</span><br><span class="line">        <span class="keyword">if</span> (isFull()) &#123;</span><br><span class="line">            <span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        Node&lt;E&gt; added = <span class="keyword">new</span> <span class="title class_">Node</span>&lt;&gt;(value, head);</span><br><span class="line">        tail.next = added;</span><br><span class="line">        tail = added;</span><br><span class="line">        size++;</span><br><span class="line">        <span class="keyword">return</span> <span class="literal">true</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> E <span class="title function_">poll</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">if</span> (isEmpty()) &#123;</span><br><span class="line">            <span class="keyword">return</span> <span class="literal">null</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        Node&lt;E&gt; first = head.next;</span><br><span class="line">        head.next = first.next;</span><br><span class="line">        <span class="keyword">if</span> (first == tail) &#123;</span><br><span class="line">            tail = head;</span><br><span class="line">        &#125;</span><br><span class="line">        size--;</span><br><span class="line">        <span class="keyword">return</span> first.value;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> E <span class="title function_">peek</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">if</span> (isEmpty()) &#123;</span><br><span class="line">            <span class="keyword">return</span> <span class="literal">null</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> head.next.value;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> <span class="type">boolean</span> <span class="title function_">isEmpty</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> head == tail;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> <span class="type">boolean</span> <span class="title function_">isFull</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> size == capacity;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> Iterator&lt;E&gt; <span class="title function_">iterator</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">new</span> <span class="title class_">Iterator</span>&lt;E&gt;() &#123;</span><br><span class="line">            Node&lt;E&gt; p = head.next;</span><br><span class="line">            <span class="meta">@Override</span></span><br><span class="line">            <span class="keyword">public</span> <span class="type">boolean</span> <span class="title function_">hasNext</span><span class="params">()</span> &#123;</span><br><span class="line">                <span class="keyword">return</span> p != head;</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="meta">@Override</span></span><br><span class="line">            <span class="keyword">public</span> E <span class="title function_">next</span><span class="params">()</span> &#123;</span><br><span class="line">                <span class="type">E</span> <span class="variable">value</span> <span class="operator">=</span> p.value;</span><br><span class="line">                p = p.next;</span><br><span class="line">                <span class="keyword">return</span> value;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>



<h3 id="环形数组实现"><a href="#环形数组实现" class="headerlink" title="环形数组实现"></a>环形数组实现</h3><p><strong>好处</strong></p>
<ol>
<li>对比普通数组，起点和终点更为自由，不用考虑数据移动</li>
<li>“环”意味着不会存在【越界】问题</li>
<li>数组性能更佳</li>
<li>环形数组比较适合实现有界队列、RingBuffer 等</li>
</ol>
<p><img src= "" data-lazy-src="http://rx6zk4j2b.hn-bkt.clouddn.com/blogs/image-20221228175413998.png" alt="image-20221228175413998"></p>
<p><strong>下标计算</strong></p>
<p>例如，数组长度是 5，当前位置是 3 ，向前走 2 步，此时下标为 $(3 + 2)%5 &#x3D; 0$</p>
<p><img src= "" data-lazy-src="http://rx6zk4j2b.hn-bkt.clouddn.com/blogs/image-20221228180357257.png" alt="image-20221228180357257"></p>
<p>$$<br>(cur + step) % length<br>$$</p>
<ul>
<li>cur 当前指针位置</li>
<li>step 前进步数</li>
<li>length 数组长度</li>
</ul>
<blockquote>
<p>注意：</p>
<ul>
<li>如果 step &#x3D; 1，也就是一次走一步，可以在 &gt;&#x3D; length 时重置为 0 即可</li>
</ul>
</blockquote>
<p><strong>判断空</strong></p>
<p><img src= "" data-lazy-src="http://rx6zk4j2b.hn-bkt.clouddn.com/blogs/image-20221231081009018.png" alt="image-20221231081009018"></p>
<p><strong>判断满</strong></p>
<p><img src= "" data-lazy-src="http://rx6zk4j2b.hn-bkt.clouddn.com/blogs/image-20221231080909475.png" alt="image-20221231080909475"></p>
<p>满之后的策略可以根据业务需求决定</p>
<ul>
<li>例如我们要实现的环形队列，满之后就拒绝入队</li>
</ul>
<p>代码</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">ArrayQueue</span>&lt;E&gt; <span class="keyword">implements</span> <span class="title class_">Queue</span>&lt;E&gt;, Iterable&lt;E&gt;&#123;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> <span class="type">int</span> <span class="variable">head</span> <span class="operator">=</span> <span class="number">0</span>;</span><br><span class="line">    <span class="keyword">private</span> <span class="type">int</span> <span class="variable">tail</span> <span class="operator">=</span> <span class="number">0</span>;</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">final</span> E[] array;</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">final</span> <span class="type">int</span> length;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@SuppressWarnings(&quot;all&quot;)</span></span><br><span class="line">    <span class="keyword">public</span> <span class="title function_">ArrayQueue</span><span class="params">(<span class="type">int</span> capacity)</span> &#123;</span><br><span class="line">        length = capacity + <span class="number">1</span>;</span><br><span class="line">        array = (E[]) <span class="keyword">new</span> <span class="title class_">Object</span>[length];</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> <span class="type">boolean</span> <span class="title function_">offer</span><span class="params">(E value)</span> &#123;</span><br><span class="line">        <span class="keyword">if</span> (isFull()) &#123;</span><br><span class="line">            <span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        array[tail] = value;</span><br><span class="line">        tail = (tail + <span class="number">1</span>) % length;</span><br><span class="line">        <span class="keyword">return</span> <span class="literal">true</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> E <span class="title function_">poll</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">if</span> (isEmpty()) &#123;</span><br><span class="line">            <span class="keyword">return</span> <span class="literal">null</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="type">E</span> <span class="variable">value</span> <span class="operator">=</span> array[head];</span><br><span class="line">        head = (head + <span class="number">1</span>) % length;</span><br><span class="line">        <span class="keyword">return</span> value;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> E <span class="title function_">peek</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">if</span> (isEmpty()) &#123;</span><br><span class="line">            <span class="keyword">return</span> <span class="literal">null</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> array[head];</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> <span class="type">boolean</span> <span class="title function_">isEmpty</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> tail == head;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> <span class="type">boolean</span> <span class="title function_">isFull</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> (tail + <span class="number">1</span>) % length == head;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> Iterator&lt;E&gt; <span class="title function_">iterator</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">new</span> <span class="title class_">Iterator</span>&lt;E&gt;() &#123;</span><br><span class="line">            <span class="type">int</span> <span class="variable">p</span> <span class="operator">=</span> head;</span><br><span class="line">            <span class="meta">@Override</span></span><br><span class="line">            <span class="keyword">public</span> <span class="type">boolean</span> <span class="title function_">hasNext</span><span class="params">()</span> &#123;</span><br><span class="line">                <span class="keyword">return</span> p != tail;</span><br><span class="line">            &#125;</span><br><span class="line"></span><br><span class="line">            <span class="meta">@Override</span></span><br><span class="line">            <span class="keyword">public</span> E <span class="title function_">next</span><span class="params">()</span> &#123;</span><br><span class="line">                <span class="type">E</span> <span class="variable">value</span> <span class="operator">=</span> array[p];</span><br><span class="line">                p = (p + <span class="number">1</span>) % array.length;</span><br><span class="line">                <span class="keyword">return</span> value;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>



<p><strong>判断空、满方法2</strong></p>
<p>引入 size </p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">ArrayQueue2</span>&lt;E&gt; <span class="keyword">implements</span> <span class="title class_">Queue</span>&lt;E&gt;, Iterable&lt;E&gt; &#123;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> <span class="type">int</span> <span class="variable">head</span> <span class="operator">=</span> <span class="number">0</span>;</span><br><span class="line">    <span class="keyword">private</span> <span class="type">int</span> <span class="variable">tail</span> <span class="operator">=</span> <span class="number">0</span>;</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">final</span> E[] array;</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">final</span> <span class="type">int</span> capacity;</span><br><span class="line">    <span class="keyword">private</span> <span class="type">int</span> <span class="variable">size</span> <span class="operator">=</span> <span class="number">0</span>;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@SuppressWarnings(&quot;all&quot;)</span></span><br><span class="line">    <span class="keyword">public</span> <span class="title function_">ArrayQueue2</span><span class="params">(<span class="type">int</span> capacity)</span> &#123;</span><br><span class="line">        <span class="built_in">this</span>.capacity = capacity;</span><br><span class="line">        array = (E[]) <span class="keyword">new</span> <span class="title class_">Object</span>[capacity];</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> <span class="type">boolean</span> <span class="title function_">offer</span><span class="params">(E value)</span> &#123;</span><br><span class="line">        <span class="keyword">if</span> (isFull()) &#123;</span><br><span class="line">            <span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        array[tail] = value;</span><br><span class="line">        tail = (tail + <span class="number">1</span>) % capacity;</span><br><span class="line">        size++;</span><br><span class="line">        <span class="keyword">return</span> <span class="literal">true</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> E <span class="title function_">poll</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">if</span> (isEmpty()) &#123;</span><br><span class="line">            <span class="keyword">return</span> <span class="literal">null</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="type">E</span> <span class="variable">value</span> <span class="operator">=</span> array[head];</span><br><span class="line">        head = (head + <span class="number">1</span>) % capacity;</span><br><span class="line">        size--;</span><br><span class="line">        <span class="keyword">return</span> value;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> E <span class="title function_">peek</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">if</span> (isEmpty()) &#123;</span><br><span class="line">            <span class="keyword">return</span> <span class="literal">null</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> array[head];</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> <span class="type">boolean</span> <span class="title function_">isEmpty</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> size == <span class="number">0</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> <span class="type">boolean</span> <span class="title function_">isFull</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> size == capacity;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> Iterator&lt;E&gt; <span class="title function_">iterator</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">new</span> <span class="title class_">Iterator</span>&lt;E&gt;() &#123;</span><br><span class="line">            <span class="type">int</span> <span class="variable">p</span> <span class="operator">=</span> head;</span><br><span class="line"></span><br><span class="line">            <span class="meta">@Override</span></span><br><span class="line">            <span class="keyword">public</span> <span class="type">boolean</span> <span class="title function_">hasNext</span><span class="params">()</span> &#123;</span><br><span class="line">                <span class="keyword">return</span> p != tail;</span><br><span class="line">            &#125;</span><br><span class="line"></span><br><span class="line">            <span class="meta">@Override</span></span><br><span class="line">            <span class="keyword">public</span> E <span class="title function_">next</span><span class="params">()</span> &#123;</span><br><span class="line">                <span class="type">E</span> <span class="variable">value</span> <span class="operator">=</span> array[p];</span><br><span class="line">                p = (p + <span class="number">1</span>) % capacity;</span><br><span class="line">                <span class="keyword">return</span> value;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>



<p><strong>判断空、满方法3</strong></p>
<ul>
<li><p>head 和 tail 不断递增，用到索引时，再用它们进行计算，两个问题</p>
<ul>
<li><p>如何保证 head 和 tail 自增超过正整数最大值的正确性</p>
</li>
<li><p>如何让取模运算性能更高</p>
</li>
</ul>
</li>
<li><p>答案：让 capacity 为 2 的幂</p>
</li>
</ul>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">ArrayQueue3</span>&lt;E&gt; <span class="keyword">implements</span> <span class="title class_">Queue</span>&lt;E&gt;, Iterable&lt;E&gt; &#123;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> <span class="type">int</span> <span class="variable">head</span> <span class="operator">=</span> <span class="number">0</span>;</span><br><span class="line">    <span class="keyword">private</span> <span class="type">int</span> <span class="variable">tail</span> <span class="operator">=</span> <span class="number">0</span>;</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">final</span> E[] array;</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">final</span> <span class="type">int</span> capacity;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@SuppressWarnings(&quot;all&quot;)</span></span><br><span class="line">    <span class="keyword">public</span> <span class="title function_">ArrayQueue3</span><span class="params">(<span class="type">int</span> capacity)</span> &#123;</span><br><span class="line">        <span class="keyword">if</span> ((capacity &amp; capacity - <span class="number">1</span>) != <span class="number">0</span>) &#123;</span><br><span class="line">            <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">IllegalArgumentException</span>(<span class="string">&quot;capacity 必须为 2 的幂&quot;</span>);</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="built_in">this</span>.capacity = capacity;</span><br><span class="line">        array = (E[]) <span class="keyword">new</span> <span class="title class_">Object</span>[<span class="built_in">this</span>.capacity];</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> <span class="type">boolean</span> <span class="title function_">offer</span><span class="params">(E value)</span> &#123;</span><br><span class="line">        <span class="keyword">if</span> (isFull()) &#123;</span><br><span class="line">            <span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        array[tail &amp; capacity - <span class="number">1</span>] = value;</span><br><span class="line">        tail++;</span><br><span class="line">        <span class="keyword">return</span> <span class="literal">true</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> E <span class="title function_">poll</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">if</span> (isEmpty()) &#123;</span><br><span class="line">            <span class="keyword">return</span> <span class="literal">null</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="type">E</span> <span class="variable">value</span> <span class="operator">=</span> array[head &amp; capacity - <span class="number">1</span>];</span><br><span class="line">        head++;</span><br><span class="line">        <span class="keyword">return</span> value;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> E <span class="title function_">peek</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">if</span> (isEmpty()) &#123;</span><br><span class="line">            <span class="keyword">return</span> <span class="literal">null</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> array[head &amp; capacity - <span class="number">1</span>];</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> <span class="type">boolean</span> <span class="title function_">isEmpty</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> tail - head == <span class="number">0</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> <span class="type">boolean</span> <span class="title function_">isFull</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> tail - head == capacity;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> Iterator&lt;E&gt; <span class="title function_">iterator</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">new</span> <span class="title class_">Iterator</span>&lt;E&gt;() &#123;</span><br><span class="line">            <span class="type">int</span> <span class="variable">p</span> <span class="operator">=</span> head;</span><br><span class="line"></span><br><span class="line">            <span class="meta">@Override</span></span><br><span class="line">            <span class="keyword">public</span> <span class="type">boolean</span> <span class="title function_">hasNext</span><span class="params">()</span> &#123;</span><br><span class="line">                <span class="keyword">return</span> p != tail;</span><br><span class="line">            &#125;</span><br><span class="line"></span><br><span class="line">            <span class="meta">@Override</span></span><br><span class="line">            <span class="keyword">public</span> E <span class="title function_">next</span><span class="params">()</span> &#123;</span><br><span class="line">                <span class="type">E</span> <span class="variable">value</span> <span class="operator">=</span> array[p &amp; capacity - <span class="number">1</span>];</span><br><span class="line">                p++;</span><br><span class="line">                <span class="keyword">return</span> value;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>



<h2 id="2-5-栈"><a href="#2-5-栈" class="headerlink" title="2.5 栈"></a>2.5 栈</h2><h3 id="概述-4"><a href="#概述-4" class="headerlink" title="概述"></a>概述</h3><p>计算机科学中，<strong>stack</strong> 是一种线性的数据结构，只能在其一端添加数据和移除数据。习惯来说，这一端称之为<strong>栈顶</strong>，另一端不能操作数据的称之为<strong>栈底</strong>，就如同生活中的一摞书</p>
<p>先提供一个栈接口</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">interface</span> <span class="title class_">Stack</span>&lt;E&gt; &#123;</span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 向栈顶压入元素</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> value 待压入值</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@return</span> 压入成功返回 true, 否则返回 false</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="type">boolean</span> <span class="title function_">push</span><span class="params">(E value)</span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 从栈顶弹出元素</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@return</span> 栈非空返回栈顶元素, 栈为空返回 null</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    E <span class="title function_">pop</span><span class="params">()</span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 返回栈顶元素, 不弹出</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@return</span> 栈非空返回栈顶元素, 栈为空返回 null</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    E <span class="title function_">peek</span><span class="params">()</span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 判断栈是否为空</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@return</span> 空返回 true, 否则返回 false</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="type">boolean</span> <span class="title function_">isEmpty</span><span class="params">()</span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 判断栈是否已满</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@return</span> 满返回 true, 否则返回 false</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="type">boolean</span> <span class="title function_">isFull</span><span class="params">()</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>



<h3 id="链表实现-1"><a href="#链表实现-1" class="headerlink" title="链表实现"></a>链表实现</h3><figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">LinkedListStack</span>&lt;E&gt; <span class="keyword">implements</span> <span class="title class_">Stack</span>&lt;E&gt;, Iterable&lt;E&gt; &#123;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">final</span> <span class="type">int</span> capacity;</span><br><span class="line">    <span class="keyword">private</span> <span class="type">int</span> size;</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">final</span> Node&lt;E&gt; head = <span class="keyword">new</span> <span class="title class_">Node</span>&lt;&gt;(<span class="literal">null</span>, <span class="literal">null</span>);</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="title function_">LinkedListStack</span><span class="params">(<span class="type">int</span> capacity)</span> &#123;</span><br><span class="line">        <span class="built_in">this</span>.capacity = capacity;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> <span class="type">boolean</span> <span class="title function_">push</span><span class="params">(E value)</span> &#123;</span><br><span class="line">        <span class="keyword">if</span> (isFull()) &#123;</span><br><span class="line">            <span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        head.next = <span class="keyword">new</span> <span class="title class_">Node</span>&lt;&gt;(value, head.next);</span><br><span class="line">        size++;</span><br><span class="line">        <span class="keyword">return</span> <span class="literal">true</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> E <span class="title function_">pop</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">if</span> (isEmpty()) &#123;</span><br><span class="line">            <span class="keyword">return</span> <span class="literal">null</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        Node&lt;E&gt; first = head.next;</span><br><span class="line">        head.next = first.next;</span><br><span class="line">        size--;</span><br><span class="line">        <span class="keyword">return</span> first.value;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> E <span class="title function_">peek</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">if</span> (isEmpty()) &#123;</span><br><span class="line">            <span class="keyword">return</span> <span class="literal">null</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> head.next.value;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> <span class="type">boolean</span> <span class="title function_">isEmpty</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> head.next == <span class="literal">null</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> <span class="type">boolean</span> <span class="title function_">isFull</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> size == capacity;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> Iterator&lt;E&gt; <span class="title function_">iterator</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">new</span> <span class="title class_">Iterator</span>&lt;E&gt;() &#123;</span><br><span class="line">            Node&lt;E&gt; p = head.next;</span><br><span class="line">            <span class="meta">@Override</span></span><br><span class="line">            <span class="keyword">public</span> <span class="type">boolean</span> <span class="title function_">hasNext</span><span class="params">()</span> &#123;</span><br><span class="line">                <span class="keyword">return</span> p != <span class="literal">null</span>;</span><br><span class="line">            &#125;</span><br><span class="line"></span><br><span class="line">            <span class="meta">@Override</span></span><br><span class="line">            <span class="keyword">public</span> E <span class="title function_">next</span><span class="params">()</span> &#123;</span><br><span class="line">                <span class="type">E</span> <span class="variable">value</span> <span class="operator">=</span> p.value;</span><br><span class="line">                p = p.next;</span><br><span class="line">                <span class="keyword">return</span> value;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">static</span> <span class="keyword">class</span> <span class="title class_">Node</span>&lt;E&gt; &#123;</span><br><span class="line">        E value;</span><br><span class="line">        Node&lt;E&gt; next;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">public</span> <span class="title function_">Node</span><span class="params">(E value, Node&lt;E&gt; next)</span> &#123;</span><br><span class="line">            <span class="built_in">this</span>.value = value;</span><br><span class="line">            <span class="built_in">this</span>.next = next;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>



<h3 id="数组实现"><a href="#数组实现" class="headerlink" title="数组实现"></a>数组实现</h3><figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">ArrayStack</span>&lt;E&gt; <span class="keyword">implements</span> <span class="title class_">Stack</span>&lt;E&gt;, Iterable&lt;E&gt;&#123;</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">final</span> E[] array;</span><br><span class="line">    <span class="keyword">private</span> <span class="type">int</span> <span class="variable">top</span> <span class="operator">=</span> <span class="number">0</span>;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@SuppressWarnings(&quot;all&quot;)</span></span><br><span class="line">    <span class="keyword">public</span> <span class="title function_">ArrayStack</span><span class="params">(<span class="type">int</span> capacity)</span> &#123;</span><br><span class="line">        <span class="built_in">this</span>.array = (E[]) <span class="keyword">new</span> <span class="title class_">Object</span>[capacity];</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> <span class="type">boolean</span> <span class="title function_">push</span><span class="params">(E value)</span> &#123;</span><br><span class="line">        <span class="keyword">if</span> (isFull()) &#123;</span><br><span class="line">            <span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        array[top++] = value;</span><br><span class="line">        <span class="keyword">return</span> <span class="literal">true</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> E <span class="title function_">pop</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">if</span> (isEmpty()) &#123;</span><br><span class="line">            <span class="keyword">return</span> <span class="literal">null</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> array[--top];</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> E <span class="title function_">peek</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">if</span> (isEmpty()) &#123;</span><br><span class="line">            <span class="keyword">return</span> <span class="literal">null</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> array[top-<span class="number">1</span>];</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> <span class="type">boolean</span> <span class="title function_">isEmpty</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> top == <span class="number">0</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> <span class="type">boolean</span> <span class="title function_">isFull</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> top == array.length;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> Iterator&lt;E&gt; <span class="title function_">iterator</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">new</span> <span class="title class_">Iterator</span>&lt;E&gt;() &#123;</span><br><span class="line">            <span class="type">int</span> <span class="variable">p</span> <span class="operator">=</span> top;</span><br><span class="line">            <span class="meta">@Override</span></span><br><span class="line">            <span class="keyword">public</span> <span class="type">boolean</span> <span class="title function_">hasNext</span><span class="params">()</span> &#123;</span><br><span class="line">                <span class="keyword">return</span> p &gt; <span class="number">0</span>;</span><br><span class="line">            &#125;</span><br><span class="line"></span><br><span class="line">            <span class="meta">@Override</span></span><br><span class="line">            <span class="keyword">public</span> E <span class="title function_">next</span><span class="params">()</span> &#123;</span><br><span class="line">                <span class="keyword">return</span> array[--p];</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>



<h3 id="应用"><a href="#应用" class="headerlink" title="应用"></a>应用</h3><p>模拟如下方法调用</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> &#123;</span><br><span class="line">    System.out.println(<span class="string">&quot;main1&quot;</span>);</span><br><span class="line">    System.out.println(<span class="string">&quot;main2&quot;</span>);</span><br><span class="line">    method1();</span><br><span class="line">    method2();</span><br><span class="line">    System.out.println(<span class="string">&quot;main3&quot;</span>);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">method1</span><span class="params">()</span> &#123;</span><br><span class="line">    System.out.println(<span class="string">&quot;method1&quot;</span>);</span><br><span class="line">    method3();</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">method2</span><span class="params">()</span> &#123;</span><br><span class="line">    System.out.println(<span class="string">&quot;method2&quot;</span>);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">method3</span><span class="params">()</span> &#123;</span><br><span class="line">    System.out.println(<span class="string">&quot;method3&quot;</span>);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>模拟代码</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">CPU</span> &#123;</span><br><span class="line">    <span class="keyword">static</span> <span class="keyword">class</span> <span class="title class_">Frame</span> &#123;</span><br><span class="line">        <span class="type">int</span> exit;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">public</span> <span class="title function_">Frame</span><span class="params">(<span class="type">int</span> exit)</span> &#123;</span><br><span class="line">            <span class="built_in">this</span>.exit = exit;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">static</span> <span class="type">int</span> <span class="variable">pc</span> <span class="operator">=</span> <span class="number">1</span>; <span class="comment">// 模拟程序计数器 Program counter</span></span><br><span class="line">    <span class="keyword">static</span> ArrayStack&lt;Frame&gt; stack = <span class="keyword">new</span> <span class="title class_">ArrayStack</span>&lt;&gt;(<span class="number">100</span>); <span class="comment">// 模拟方法调用栈</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> &#123;</span><br><span class="line">        stack.push(<span class="keyword">new</span> <span class="title class_">Frame</span>(-<span class="number">1</span>));</span><br><span class="line">        <span class="keyword">while</span> (!stack.isEmpty()) &#123;</span><br><span class="line">            <span class="keyword">switch</span> (pc) &#123;</span><br><span class="line">                <span class="keyword">case</span> <span class="number">1</span> -&gt; &#123;</span><br><span class="line">                    System.out.println(<span class="string">&quot;main1&quot;</span>);</span><br><span class="line">                    pc++;</span><br><span class="line">                &#125;</span><br><span class="line">                <span class="keyword">case</span> <span class="number">2</span> -&gt; &#123;</span><br><span class="line">                    System.out.println(<span class="string">&quot;main2&quot;</span>);</span><br><span class="line">                    pc++;</span><br><span class="line">                &#125;</span><br><span class="line">                <span class="keyword">case</span> <span class="number">3</span> -&gt; &#123;</span><br><span class="line">                    stack.push(<span class="keyword">new</span> <span class="title class_">Frame</span>(pc + <span class="number">1</span>));</span><br><span class="line">                    pc = <span class="number">100</span>;</span><br><span class="line">                &#125;</span><br><span class="line">                <span class="keyword">case</span> <span class="number">4</span> -&gt; &#123;</span><br><span class="line">                    stack.push(<span class="keyword">new</span> <span class="title class_">Frame</span>(pc + <span class="number">1</span>));</span><br><span class="line">                    pc = <span class="number">200</span>;</span><br><span class="line">                &#125;</span><br><span class="line">                <span class="keyword">case</span> <span class="number">5</span> -&gt; &#123;</span><br><span class="line">                    System.out.println(<span class="string">&quot;main3&quot;</span>);</span><br><span class="line">                    pc = stack.pop().exit;</span><br><span class="line">                &#125;</span><br><span class="line">                <span class="keyword">case</span> <span class="number">100</span> -&gt; &#123;</span><br><span class="line">                    System.out.println(<span class="string">&quot;method1&quot;</span>);</span><br><span class="line">                    stack.push(<span class="keyword">new</span> <span class="title class_">Frame</span>(pc + <span class="number">1</span>));</span><br><span class="line">                    pc = <span class="number">300</span>;</span><br><span class="line">                &#125;</span><br><span class="line">                <span class="keyword">case</span> <span class="number">101</span> -&gt; &#123;</span><br><span class="line">                    pc = stack.pop().exit;</span><br><span class="line">                &#125;</span><br><span class="line">                <span class="keyword">case</span> <span class="number">200</span> -&gt; &#123;</span><br><span class="line">                    System.out.println(<span class="string">&quot;method2&quot;</span>);</span><br><span class="line">                    pc = stack.pop().exit;</span><br><span class="line">                &#125;</span><br><span class="line">                <span class="keyword">case</span> <span class="number">300</span> -&gt; &#123;</span><br><span class="line">                    System.out.println(<span class="string">&quot;method3&quot;</span>);</span><br><span class="line">                    pc = stack.pop().exit;</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>



<h2 id="2-6-双端队列"><a href="#2-6-双端队列" class="headerlink" title="2.6 双端队列"></a>2.6 双端队列</h2><h3 id="概述-5"><a href="#概述-5" class="headerlink" title="概述"></a>概述</h3><p>双端队列、队列、栈对比</p>
<table>
<thead>
<tr>
<th></th>
<th>定义</th>
<th>特点</th>
</tr>
</thead>
<tbody><tr>
<td>队列</td>
<td>一端删除（头）另一端添加（尾）</td>
<td>First In First Out</td>
</tr>
<tr>
<td>栈</td>
<td>一端删除和添加（顶）</td>
<td>Last In First Out</td>
</tr>
<tr>
<td>双端队列</td>
<td>两端都可以删除、添加</td>
<td></td>
</tr>
<tr>
<td>优先级队列</td>
<td></td>
<td>优先级高者先出队</td>
</tr>
<tr>
<td>延时队列</td>
<td></td>
<td>根据延时时间确定优先级</td>
</tr>
<tr>
<td>并发非阻塞队列</td>
<td>队列空或满时不阻塞</td>
<td></td>
</tr>
<tr>
<td>并发阻塞队列</td>
<td>队列空时删除阻塞、队列满时添加阻塞</td>
<td></td>
</tr>
</tbody></table>
<blockquote>
<p>注1：</p>
<ul>
<li>Java 中 LinkedList 即为典型双端队列实现，不过它同时实现了 Queue 接口，也提供了栈的 push pop 等方法</li>
</ul>
<p>注2：</p>
<ul>
<li><p>不同语言，操作双端队列的方法命名有所不同，参见下表</p>
<table>
<thead>
<tr>
<th>操作</th>
<th>Java</th>
<th>JavaScript</th>
<th>C++</th>
<th>leetCode 641</th>
</tr>
</thead>
<tbody><tr>
<td>尾部插入</td>
<td>offerLast</td>
<td>push</td>
<td>push_back</td>
<td>insertLast</td>
</tr>
<tr>
<td>头部插入</td>
<td>offerFirst</td>
<td>unshift</td>
<td>push_front</td>
<td>insertFront</td>
</tr>
<tr>
<td>尾部移除</td>
<td>pollLast</td>
<td>pop</td>
<td>pop_back</td>
<td>deleteLast</td>
</tr>
<tr>
<td>头部移除</td>
<td>pollFirst</td>
<td>shift</td>
<td>pop_front</td>
<td>deleteFront</td>
</tr>
<tr>
<td>尾部获取</td>
<td>peekLast</td>
<td>at(-1)</td>
<td>back</td>
<td>getRear</td>
</tr>
<tr>
<td>头部获取</td>
<td>peekFirst</td>
<td>at(0)</td>
<td>front</td>
<td>getFront</td>
</tr>
</tbody></table>
</li>
<li><p>吐槽一下 leetCode 命名比较 low</p>
</li>
<li><p>常见的单词还有 enqueue 入队、dequeue 出队</p>
</li>
</ul>
</blockquote>
<p><strong>接口定义</strong></p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">interface</span> <span class="title class_">Deque</span>&lt;E&gt; &#123;</span><br><span class="line"></span><br><span class="line">    <span class="type">boolean</span> <span class="title function_">offerFirst</span><span class="params">(E e)</span>;</span><br><span class="line"></span><br><span class="line">    <span class="type">boolean</span> <span class="title function_">offerLast</span><span class="params">(E e)</span>;</span><br><span class="line"></span><br><span class="line">    E <span class="title function_">pollFirst</span><span class="params">()</span>;</span><br><span class="line"></span><br><span class="line">    E <span class="title function_">pollLast</span><span class="params">()</span>;</span><br><span class="line"></span><br><span class="line">    E <span class="title function_">peekFirst</span><span class="params">()</span>;</span><br><span class="line"></span><br><span class="line">    E <span class="title function_">peekLast</span><span class="params">()</span>;</span><br><span class="line">    </span><br><span class="line">    <span class="type">boolean</span> <span class="title function_">isEmpty</span><span class="params">()</span>;</span><br><span class="line"></span><br><span class="line">    <span class="type">boolean</span> <span class="title function_">isFull</span><span class="params">()</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>



<h3 id="链表实现-2"><a href="#链表实现-2" class="headerlink" title="链表实现"></a>链表实现</h3><figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 基于环形链表的双端队列</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> &lt;E&gt; 元素类型</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">LinkedListDeque</span>&lt;E&gt; <span class="keyword">implements</span> <span class="title class_">Deque</span>&lt;E&gt;, Iterable&lt;E&gt; &#123;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> <span class="type">boolean</span> <span class="title function_">offerFirst</span><span class="params">(E e)</span> &#123;</span><br><span class="line">        <span class="keyword">if</span> (isFull()) &#123;</span><br><span class="line">            <span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        size++;</span><br><span class="line">        Node&lt;E&gt; a = sentinel;</span><br><span class="line">        Node&lt;E&gt; b = sentinel.next;</span><br><span class="line">        Node&lt;E&gt; offered = <span class="keyword">new</span> <span class="title class_">Node</span>&lt;&gt;(a, e, b);</span><br><span class="line">        a.next = offered;</span><br><span class="line">        b.prev = offered;</span><br><span class="line">        <span class="keyword">return</span> <span class="literal">true</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> <span class="type">boolean</span> <span class="title function_">offerLast</span><span class="params">(E e)</span> &#123;</span><br><span class="line">        <span class="keyword">if</span> (isFull()) &#123;</span><br><span class="line">            <span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        size++;</span><br><span class="line">        Node&lt;E&gt; a = sentinel.prev;</span><br><span class="line">        Node&lt;E&gt; b = sentinel;</span><br><span class="line">        Node&lt;E&gt; offered = <span class="keyword">new</span> <span class="title class_">Node</span>&lt;&gt;(a, e, b);</span><br><span class="line">        a.next = offered;</span><br><span class="line">        b.prev = offered;</span><br><span class="line">        <span class="keyword">return</span> <span class="literal">true</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> E <span class="title function_">pollFirst</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">if</span> (isEmpty()) &#123;</span><br><span class="line">            <span class="keyword">return</span> <span class="literal">null</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        Node&lt;E&gt; a = sentinel;</span><br><span class="line">        Node&lt;E&gt; polled = sentinel.next;</span><br><span class="line">        Node&lt;E&gt; b = polled.next;</span><br><span class="line">        a.next = b;</span><br><span class="line">        b.prev = a;</span><br><span class="line">        size--;</span><br><span class="line">        <span class="keyword">return</span> polled.value;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> E <span class="title function_">pollLast</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">if</span> (isEmpty()) &#123;</span><br><span class="line">            <span class="keyword">return</span> <span class="literal">null</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        Node&lt;E&gt; polled = sentinel.prev;</span><br><span class="line">        Node&lt;E&gt; a = polled.prev;</span><br><span class="line">        Node&lt;E&gt; b = sentinel;</span><br><span class="line">        a.next = b;</span><br><span class="line">        b.prev = a;</span><br><span class="line">        size--;</span><br><span class="line">        <span class="keyword">return</span> polled.value;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> E <span class="title function_">peekFirst</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">if</span> (isEmpty()) &#123;</span><br><span class="line">            <span class="keyword">return</span> <span class="literal">null</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> sentinel.next.value;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> E <span class="title function_">peekLast</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">if</span> (isEmpty()) &#123;</span><br><span class="line">            <span class="keyword">return</span> <span class="literal">null</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> sentinel.prev.value;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> <span class="type">boolean</span> <span class="title function_">isEmpty</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> size == <span class="number">0</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> <span class="type">boolean</span> <span class="title function_">isFull</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> size == capacity;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> Iterator&lt;E&gt; <span class="title function_">iterator</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">new</span> <span class="title class_">Iterator</span>&lt;E&gt;() &#123;</span><br><span class="line">            Node&lt;E&gt; p = sentinel.next;</span><br><span class="line">            <span class="meta">@Override</span></span><br><span class="line">            <span class="keyword">public</span> <span class="type">boolean</span> <span class="title function_">hasNext</span><span class="params">()</span> &#123;</span><br><span class="line">                <span class="keyword">return</span> p != sentinel;</span><br><span class="line">            &#125;</span><br><span class="line"></span><br><span class="line">            <span class="meta">@Override</span></span><br><span class="line">            <span class="keyword">public</span> E <span class="title function_">next</span><span class="params">()</span> &#123;</span><br><span class="line">                <span class="type">E</span> <span class="variable">value</span> <span class="operator">=</span> p.value;</span><br><span class="line">                p = p.next;</span><br><span class="line">                <span class="keyword">return</span> value;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">static</span> <span class="keyword">class</span> <span class="title class_">Node</span>&lt;E&gt; &#123;</span><br><span class="line">        Node&lt;E&gt; prev;</span><br><span class="line">        E value;</span><br><span class="line">        Node&lt;E&gt; next;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">public</span> <span class="title function_">Node</span><span class="params">(Node&lt;E&gt; prev, E value, Node&lt;E&gt; next)</span> &#123;</span><br><span class="line">            <span class="built_in">this</span>.prev = prev;</span><br><span class="line">            <span class="built_in">this</span>.value = value;</span><br><span class="line">            <span class="built_in">this</span>.next = next;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    Node&lt;E&gt; sentinel = <span class="keyword">new</span> <span class="title class_">Node</span>&lt;&gt;(<span class="literal">null</span>, <span class="literal">null</span>, <span class="literal">null</span>);</span><br><span class="line">    <span class="type">int</span> capacity;</span><br><span class="line">    <span class="type">int</span> size;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="title function_">LinkedListDeque</span><span class="params">(<span class="type">int</span> capacity)</span> &#123;</span><br><span class="line">        sentinel.next = sentinel;</span><br><span class="line">        sentinel.prev = sentinel;</span><br><span class="line">        <span class="built_in">this</span>.capacity = capacity;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>



<h3 id="数组实现-1"><a href="#数组实现-1" class="headerlink" title="数组实现"></a>数组实现</h3><figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 基于循环数组实现, 特点</span></span><br><span class="line"><span class="comment"> * &lt;ul&gt;</span></span><br><span class="line"><span class="comment"> *     &lt;li&gt;tail 停下来的位置不存储, 会浪费一个位置&lt;/li&gt;</span></span><br><span class="line"><span class="comment"> * &lt;/ul&gt;</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> &lt;E&gt;</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">ArrayDeque1</span>&lt;E&gt; <span class="keyword">implements</span> <span class="title class_">Deque</span>&lt;E&gt;, Iterable&lt;E&gt; &#123;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/*</span></span><br><span class="line"><span class="comment">                    h</span></span><br><span class="line"><span class="comment">            t</span></span><br><span class="line"><span class="comment">        0   1   2   3</span></span><br><span class="line"><span class="comment">        b           a</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> <span class="type">boolean</span> <span class="title function_">offerFirst</span><span class="params">(E e)</span> &#123;</span><br><span class="line">        <span class="keyword">if</span> (isFull()) &#123;</span><br><span class="line">            <span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        head = dec(head, array.length);</span><br><span class="line">        array[head] = e;</span><br><span class="line">        <span class="keyword">return</span> <span class="literal">true</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> <span class="type">boolean</span> <span class="title function_">offerLast</span><span class="params">(E e)</span> &#123;</span><br><span class="line">        <span class="keyword">if</span> (isFull()) &#123;</span><br><span class="line">            <span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        array[tail] = e;</span><br><span class="line">        tail = inc(tail, array.length);</span><br><span class="line">        <span class="keyword">return</span> <span class="literal">true</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> E <span class="title function_">pollFirst</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">if</span> (isEmpty()) &#123;</span><br><span class="line">            <span class="keyword">return</span> <span class="literal">null</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="type">E</span> <span class="variable">e</span> <span class="operator">=</span> array[head];</span><br><span class="line">        array[head] = <span class="literal">null</span>;</span><br><span class="line">        head = inc(head, array.length);</span><br><span class="line">        <span class="keyword">return</span> e;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> E <span class="title function_">pollLast</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">if</span> (isEmpty()) &#123;</span><br><span class="line">            <span class="keyword">return</span> <span class="literal">null</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        tail = dec(tail, array.length);</span><br><span class="line">        <span class="type">E</span> <span class="variable">e</span> <span class="operator">=</span> array[tail];</span><br><span class="line">        array[tail] = <span class="literal">null</span>;</span><br><span class="line">        <span class="keyword">return</span> e;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> E <span class="title function_">peekFirst</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">if</span> (isEmpty()) &#123;</span><br><span class="line">            <span class="keyword">return</span> <span class="literal">null</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> array[head];</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> E <span class="title function_">peekLast</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">if</span> (isEmpty()) &#123;</span><br><span class="line">            <span class="keyword">return</span> <span class="literal">null</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> array[dec(tail, array.length)];</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> <span class="type">boolean</span> <span class="title function_">isEmpty</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> head == tail;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> <span class="type">boolean</span> <span class="title function_">isFull</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">if</span> (tail &gt; head) &#123;</span><br><span class="line">            <span class="keyword">return</span> tail - head == array.length - <span class="number">1</span>;</span><br><span class="line">        &#125; <span class="keyword">else</span> <span class="keyword">if</span> (tail &lt; head) &#123;</span><br><span class="line">            <span class="keyword">return</span> head - tail == <span class="number">1</span>;</span><br><span class="line">        &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">            <span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> Iterator&lt;E&gt; <span class="title function_">iterator</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">new</span> <span class="title class_">Iterator</span>&lt;E&gt;() &#123;</span><br><span class="line">            <span class="type">int</span> <span class="variable">p</span> <span class="operator">=</span> head;</span><br><span class="line">            <span class="meta">@Override</span></span><br><span class="line">            <span class="keyword">public</span> <span class="type">boolean</span> <span class="title function_">hasNext</span><span class="params">()</span> &#123;</span><br><span class="line">                <span class="keyword">return</span> p != tail;</span><br><span class="line">            &#125;</span><br><span class="line"></span><br><span class="line">            <span class="meta">@Override</span></span><br><span class="line">            <span class="keyword">public</span> E <span class="title function_">next</span><span class="params">()</span> &#123;</span><br><span class="line">                <span class="type">E</span> <span class="variable">e</span> <span class="operator">=</span> array[p];</span><br><span class="line">                p = inc(p, array.length);</span><br><span class="line">                <span class="keyword">return</span> e;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    E[] array;</span><br><span class="line">    <span class="type">int</span> head;</span><br><span class="line">    <span class="type">int</span> tail;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@SuppressWarnings(&quot;unchecked&quot;)</span></span><br><span class="line">    <span class="keyword">public</span> <span class="title function_">ArrayDeque1</span><span class="params">(<span class="type">int</span> capacity)</span> &#123;</span><br><span class="line">        array = (E[]) <span class="keyword">new</span> <span class="title class_">Object</span>[capacity + <span class="number">1</span>];</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">static</span> <span class="type">int</span> <span class="title function_">inc</span><span class="params">(<span class="type">int</span> i, <span class="type">int</span> length)</span> &#123;</span><br><span class="line">        <span class="keyword">if</span> (i + <span class="number">1</span> &gt;= length) &#123;</span><br><span class="line">            <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> i + <span class="number">1</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">static</span> <span class="type">int</span> <span class="title function_">dec</span><span class="params">(<span class="type">int</span> i, <span class="type">int</span> length)</span> &#123;</span><br><span class="line">        <span class="keyword">if</span> (i - <span class="number">1</span> &lt; <span class="number">0</span>) &#123;</span><br><span class="line">            <span class="keyword">return</span> length - <span class="number">1</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> i - <span class="number">1</span>;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>数组实现中，如果存储的是基本类型，那么无需考虑内存释放，例如</p>
<p><img src= "" data-lazy-src="http://rx6zk4j2b.hn-bkt.clouddn.com/blogs/image-20230110084245095.png" alt="image-20230110084245095"></p>
<p>但如果存储的是引用类型，应当设置该位置的引用为 null，以便内存及时释放</p>
<p><img src= "" data-lazy-src="http://rx6zk4j2b.hn-bkt.clouddn.com/blogs/image-20230110084632543.png" alt="image-20230110084632543"></p>
<h2 id="2-7-优先级队列"><a href="#2-7-优先级队列" class="headerlink" title="2.7 优先级队列"></a>2.7 优先级队列</h2><h3 id="无序数组实现"><a href="#无序数组实现" class="headerlink" title="无序数组实现"></a>无序数组实现</h3><p><strong>要点</strong></p>
<ol>
<li>入队保持顺序</li>
<li>出队前找到优先级最高的出队，相当于一次选择排序</li>
</ol>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">PriorityQueue1</span>&lt;E <span class="keyword">extends</span> <span class="title class_">Priority</span>&gt; <span class="keyword">implements</span> <span class="title class_">Queue</span>&lt;E&gt; &#123;</span><br><span class="line"></span><br><span class="line">    Priority[] array;</span><br><span class="line">    <span class="type">int</span> size;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="title function_">PriorityQueue1</span><span class="params">(<span class="type">int</span> capacity)</span> &#123;</span><br><span class="line">        array = <span class="keyword">new</span> <span class="title class_">Priority</span>[capacity];</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span> <span class="comment">// O(1)</span></span><br><span class="line">    <span class="keyword">public</span> <span class="type">boolean</span> <span class="title function_">offer</span><span class="params">(E e)</span> &#123;</span><br><span class="line">        <span class="keyword">if</span> (isFull()) &#123;</span><br><span class="line">            <span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        array[size++] = e;</span><br><span class="line">        <span class="keyword">return</span> <span class="literal">true</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 返回优先级最高的索引值</span></span><br><span class="line">    <span class="keyword">private</span> <span class="type">int</span> <span class="title function_">selectMax</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="type">int</span> <span class="variable">max</span> <span class="operator">=</span> <span class="number">0</span>;</span><br><span class="line">        <span class="keyword">for</span> (<span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> <span class="number">1</span>; i &lt; size; i++) &#123;</span><br><span class="line">            <span class="keyword">if</span> (array[i].priority() &gt; array[max].priority()) &#123;</span><br><span class="line">                max = i;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> max;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span> <span class="comment">// O(n)</span></span><br><span class="line">    <span class="keyword">public</span> E <span class="title function_">poll</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">if</span> (isEmpty()) &#123;</span><br><span class="line">            <span class="keyword">return</span> <span class="literal">null</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="type">int</span> <span class="variable">max</span> <span class="operator">=</span> selectMax();</span><br><span class="line">        <span class="type">E</span> <span class="variable">e</span> <span class="operator">=</span> (E) array[max];</span><br><span class="line">        remove(max);</span><br><span class="line">        <span class="keyword">return</span> e;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">void</span> <span class="title function_">remove</span><span class="params">(<span class="type">int</span> index)</span> &#123;</span><br><span class="line">        <span class="keyword">if</span> (index &lt; size - <span class="number">1</span>) &#123;</span><br><span class="line">            System.arraycopy(array, index + <span class="number">1</span>,</span><br><span class="line">                    array, index, size - <span class="number">1</span> - index);</span><br><span class="line">        &#125;</span><br><span class="line">        array[--size] = <span class="literal">null</span>; <span class="comment">// help GC</span></span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> E <span class="title function_">peek</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">if</span> (isEmpty()) &#123;</span><br><span class="line">            <span class="keyword">return</span> <span class="literal">null</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="type">int</span> <span class="variable">max</span> <span class="operator">=</span> selectMax();</span><br><span class="line">        <span class="keyword">return</span> (E) array[max];</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> <span class="type">boolean</span> <span class="title function_">isEmpty</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> size == <span class="number">0</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> <span class="type">boolean</span> <span class="title function_">isFull</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> size == array.length;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<ul>
<li>视频中忘记了 help GC，注意一下</li>
</ul>
<h3 id="有序数组实现"><a href="#有序数组实现" class="headerlink" title="有序数组实现"></a>有序数组实现</h3><p><strong>要点</strong></p>
<ol>
<li>入队后排好序，优先级最高的排列在尾部</li>
<li>出队只需删除尾部元素即可</li>
</ol>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">PriorityQueue2</span>&lt;E <span class="keyword">extends</span> <span class="title class_">Priority</span>&gt; <span class="keyword">implements</span> <span class="title class_">Queue</span>&lt;E&gt; &#123;</span><br><span class="line"></span><br><span class="line">    Priority[] array;</span><br><span class="line">    <span class="type">int</span> size;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="title function_">PriorityQueue2</span><span class="params">(<span class="type">int</span> capacity)</span> &#123;</span><br><span class="line">        array = <span class="keyword">new</span> <span class="title class_">Priority</span>[capacity];</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// O(n)</span></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> <span class="type">boolean</span> <span class="title function_">offer</span><span class="params">(E e)</span> &#123;</span><br><span class="line">        <span class="keyword">if</span> (isFull()) &#123;</span><br><span class="line">            <span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        insert(e);</span><br><span class="line">        size++;</span><br><span class="line">        <span class="keyword">return</span> <span class="literal">true</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 一轮插入排序</span></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">void</span> <span class="title function_">insert</span><span class="params">(E e)</span> &#123;</span><br><span class="line">        <span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> size - <span class="number">1</span>;</span><br><span class="line">        <span class="keyword">while</span> (i &gt;= <span class="number">0</span> &amp;&amp; array[i].priority() &gt; e.priority()) &#123;</span><br><span class="line">            array[i + <span class="number">1</span>] = array[i];</span><br><span class="line">            i--;</span><br><span class="line">        &#125;</span><br><span class="line">        array[i + <span class="number">1</span>] = e;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// O(1)</span></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> E <span class="title function_">poll</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">if</span> (isEmpty()) &#123;</span><br><span class="line">            <span class="keyword">return</span> <span class="literal">null</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="type">E</span> <span class="variable">e</span> <span class="operator">=</span> (E) array[size - <span class="number">1</span>];</span><br><span class="line">        array[--size] = <span class="literal">null</span>; <span class="comment">// help GC</span></span><br><span class="line">        <span class="keyword">return</span> e;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> E <span class="title function_">peek</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">if</span> (isEmpty()) &#123;</span><br><span class="line">            <span class="keyword">return</span> <span class="literal">null</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> (E) array[size - <span class="number">1</span>];</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> <span class="type">boolean</span> <span class="title function_">isEmpty</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> size == <span class="number">0</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> <span class="type">boolean</span> <span class="title function_">isFull</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> size == array.length;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>



<h3 id="堆实现"><a href="#堆实现" class="headerlink" title="堆实现"></a>堆实现</h3><p>计算机科学中，堆是一种基于树的数据结构，通常用<strong>完全二叉树</strong>实现。堆的特性如下</p>
<ul>
<li>在大顶堆中，任意节点 C 与它的父节点 P 符合 $P.value \geq C.value$</li>
<li>而小顶堆中，任意节点 C 与它的父节点 P 符合 $P.value \leq C.value$</li>
<li>最顶层的节点（没有父亲）称之为 root 根节点</li>
</ul>
<blockquote>
<p>In computer science, a <strong>heap</strong> is a specialized tree-based data structure which is essentially an almost complete tree that satisfies the <strong>heap property</strong>: in a <em>max heap</em>, for any given node C, if P is a parent node of C, then the <em>key</em> (the <em>value</em>) of P is greater than or equal to the key of C. In a <em>min heap</em>, the key of P is less than or equal to the key of C. The node at the “top” of the heap (with no parents) is called the <strong>root</strong> node</p>
</blockquote>
<p>例1 - 满二叉树（Full Binary Tree）特点：每一层都是填满的</p>
<p><img src= "" data-lazy-src="http://rx6zk4j2b.hn-bkt.clouddn.com/blogs/image-20230112171444699.png" alt="image-20230112171444699"></p>
<p>例2 - 完全二叉树（Complete Binary Tree）特点：最后一层可能未填满，靠左对齐</p>
<p><img src= "" data-lazy-src="http://rx6zk4j2b.hn-bkt.clouddn.com/blogs/image-20230112171917135.png" alt="image-20230112171917135"></p>
<p>例3 - 大顶堆</p>
<p><img src= "" data-lazy-src="http://rx6zk4j2b.hn-bkt.clouddn.com/blogs/image-20230112170242265.png" alt="image-20230112170242265"></p>
<p>例4 - 小顶堆</p>
<p><img src= "" data-lazy-src="http://rx6zk4j2b.hn-bkt.clouddn.com/blogs/image-20230112171236067.png" alt="image-20230112171236067"></p>
<p>完全二叉树可以使用数组来表示</p>
<p><img src= "" data-lazy-src="http://rx6zk4j2b.hn-bkt.clouddn.com/blogs/image-20230112174351649.png" alt="image-20230112174351649"></p>
<p>特征</p>
<ul>
<li>如果从索引 0 开始存储节点数据<ul>
<li>节点 $i$ 的父节点为 $floor((i-1)&#x2F;2)$，当 $i&gt;0$ 时</li>
<li>节点 $i$ 的左子节点为 $2i+1$，右子节点为 $2i+2$，当然它们得 $&lt; size$</li>
</ul>
</li>
<li>如果从索引 1 开始存储节点数据<ul>
<li>节点 $i$ 的父节点为 $floor(i&#x2F;2)$，当 $i &gt; 1$ 时</li>
<li>节点 $i$ 的左子节点为 $2i$，右子节点为 $2i+1$，同样得 $&lt; size$</li>
</ul>
</li>
</ul>
<p>代码</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">PriorityQueue4</span>&lt;E <span class="keyword">extends</span> <span class="title class_">Priority</span>&gt; <span class="keyword">implements</span> <span class="title class_">Queue</span>&lt;E&gt; &#123;</span><br><span class="line"></span><br><span class="line">    Priority[] array;</span><br><span class="line">    <span class="type">int</span> size;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="title function_">PriorityQueue4</span><span class="params">(<span class="type">int</span> capacity)</span> &#123;</span><br><span class="line">        array = <span class="keyword">new</span> <span class="title class_">Priority</span>[capacity];</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> <span class="type">boolean</span> <span class="title function_">offer</span><span class="params">(E offered)</span> &#123;</span><br><span class="line">        <span class="keyword">if</span> (isFull()) &#123;</span><br><span class="line">            <span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="type">int</span> <span class="variable">child</span> <span class="operator">=</span> size++;</span><br><span class="line">        <span class="type">int</span> <span class="variable">parent</span> <span class="operator">=</span> (child - <span class="number">1</span>) / <span class="number">2</span>;</span><br><span class="line">        <span class="keyword">while</span> (child &gt; <span class="number">0</span> &amp;&amp; offered.priority() &gt; array[parent].priority()) &#123;</span><br><span class="line">            array[child] = array[parent];</span><br><span class="line">            child = parent;</span><br><span class="line">            parent = (child - <span class="number">1</span>) / <span class="number">2</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        array[child] = offered;</span><br><span class="line">        <span class="keyword">return</span> <span class="literal">true</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">void</span> <span class="title function_">swap</span><span class="params">(<span class="type">int</span> i, <span class="type">int</span> j)</span> &#123;</span><br><span class="line">        <span class="type">Priority</span> <span class="variable">t</span> <span class="operator">=</span> array[i];</span><br><span class="line">        array[i] = array[j];</span><br><span class="line">        array[j] = t;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> E <span class="title function_">poll</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">if</span> (isEmpty()) &#123;</span><br><span class="line">            <span class="keyword">return</span> <span class="literal">null</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        swap(<span class="number">0</span>, size - <span class="number">1</span>);</span><br><span class="line">        size--;</span><br><span class="line">        <span class="type">Priority</span> <span class="variable">e</span> <span class="operator">=</span> array[size];</span><br><span class="line">        array[size] = <span class="literal">null</span>;</span><br><span class="line">        </span><br><span class="line">        shiftDown(<span class="number">0</span>);        </span><br><span class="line">        <span class="keyword">return</span> (E) e;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">void</span> <span class="title function_">shiftDown</span><span class="params">(<span class="type">int</span> parent)</span> &#123;</span><br><span class="line">        <span class="type">int</span> <span class="variable">left</span> <span class="operator">=</span> <span class="number">2</span> * parent + <span class="number">1</span>;</span><br><span class="line">        <span class="type">int</span> <span class="variable">right</span> <span class="operator">=</span> left + <span class="number">1</span>;</span><br><span class="line">        <span class="type">int</span> <span class="variable">max</span> <span class="operator">=</span> parent;</span><br><span class="line">        <span class="keyword">if</span> (left &lt; size &amp;&amp; array[left].priority() &gt; array[max].priority()) &#123;</span><br><span class="line">            max = left;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">if</span> (right &lt; size &amp;&amp; array[right].priority() &gt; array[max].priority()) &#123;</span><br><span class="line">            max = right;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">if</span> (max != parent) &#123;</span><br><span class="line">            swap(max, parent);</span><br><span class="line">            shiftDown(max);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> E <span class="title function_">peek</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">if</span> (isEmpty()) &#123;</span><br><span class="line">            <span class="keyword">return</span> <span class="literal">null</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> (E) array[<span class="number">0</span>];</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> <span class="type">boolean</span> <span class="title function_">isEmpty</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> size == <span class="number">0</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> <span class="type">boolean</span> <span class="title function_">isFull</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> size == array.length;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>



<h2 id="2-8-阻塞队列"><a href="#2-8-阻塞队列" class="headerlink" title="2.8 阻塞队列"></a>2.8 阻塞队列</h2><p>之前的队列在很多场景下都不能很好地工作，例如</p>
<ol>
<li>大部分场景要求分离向队列放入（生产者）、从队列拿出（消费者）两个角色、它们得由不同的线程来担当，而之前的实现根本没有考虑线程安全问题</li>
<li>队列为空，那么在之前的实现里会返回 null，如果就是硬要拿到一个元素呢？只能不断循环尝试</li>
<li>队列为满，那么再之前的实现里会返回 false，如果就是硬要塞入一个元素呢？只能不断循环尝试</li>
</ol>
<p>因此我们需要解决的问题有</p>
<ol>
<li>用锁保证线程安全</li>
<li>用条件变量让<strong>等待非空线程</strong>与<strong>等待不满线程</strong>进入<strong>等待</strong>状态，而不是不断循环尝试，让 CPU 空转</li>
</ol>
<p>有同学对线程安全还没有足够的认识，下面举一个反例，两个线程都要执行入队操作（几乎在同一时刻）</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">TestThreadUnsafe</span> &#123;</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">final</span> String[] array = <span class="keyword">new</span> <span class="title class_">String</span>[<span class="number">10</span>];</span><br><span class="line">    <span class="keyword">private</span> <span class="type">int</span> <span class="variable">tail</span> <span class="operator">=</span> <span class="number">0</span>;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">offer</span><span class="params">(String e)</span> &#123;</span><br><span class="line">        array[tail] = e;</span><br><span class="line">        tail++;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> String <span class="title function_">toString</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> Arrays.toString(array);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> &#123;</span><br><span class="line">        <span class="type">TestThreadUnsafe</span> <span class="variable">queue</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">TestThreadUnsafe</span>();</span><br><span class="line">        <span class="keyword">new</span> <span class="title class_">Thread</span>(()-&gt; queue.offer(<span class="string">&quot;e1&quot;</span>), <span class="string">&quot;t1&quot;</span>).start();</span><br><span class="line">        <span class="keyword">new</span> <span class="title class_">Thread</span>(()-&gt; queue.offer(<span class="string">&quot;e2&quot;</span>), <span class="string">&quot;t2&quot;</span>).start();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>执行的时间序列如下，假设初始状态 tail &#x3D; 0，在执行过程中由于 CPU 在两个线程之间切换，造成了指令交错</p>
<table>
<thead>
<tr>
<th>线程1</th>
<th>线程2</th>
<th>说明</th>
</tr>
</thead>
<tbody><tr>
<td>array[tail]&#x3D;e1</td>
<td></td>
<td>线程1 向 tail 位置加入 e1 这个元素，但还没来得及执行  tail++</td>
</tr>
<tr>
<td></td>
<td>array[tail]&#x3D;e2</td>
<td>线程2 向 tail 位置加入 e2 这个元素，覆盖掉了 e1</td>
</tr>
<tr>
<td></td>
<td>tail++</td>
<td>tail 自增为1</td>
</tr>
<tr>
<td>tail++</td>
<td></td>
<td>tail 自增为2</td>
</tr>
<tr>
<td></td>
<td></td>
<td>最后状态 tail 为 2，数组为 [e2, null, null …]</td>
</tr>
</tbody></table>
<p>糟糕的是，由于指令交错的顺序不同，得到的结果不止以上一种，宏观上造成混乱的效果</p>
<h3 id="单锁实现"><a href="#单锁实现" class="headerlink" title="单锁实现"></a>单锁实现</h3><p>Java 中要防止代码段交错执行，需要使用锁，有两种选择</p>
<ul>
<li>synchronized 代码块，属于关键字级别提供锁保护，功能少</li>
<li>ReentrantLock 类，功能丰富</li>
</ul>
<p>以 ReentrantLock 为例</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="type">ReentrantLock</span> <span class="variable">lock</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">ReentrantLock</span>();</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">offer</span><span class="params">(String e)</span> &#123;</span><br><span class="line">    lock.lockInterruptibly();</span><br><span class="line">    <span class="keyword">try</span> &#123;</span><br><span class="line">        array[tail] = e;</span><br><span class="line">        tail++;</span><br><span class="line">    &#125; <span class="keyword">finally</span> &#123;</span><br><span class="line">        lock.unlock();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>只要两个线程执行上段代码时，锁对象是同一个，就能保证 try 块内的代码的执行不会出现指令交错现象，即执行顺序只可能是下面两种情况之一</p>
<table>
<thead>
<tr>
<th>线程1</th>
<th>线程2</th>
<th>说明</th>
</tr>
</thead>
<tbody><tr>
<td>lock.lockInterruptibly()</td>
<td></td>
<td>t1对锁对象上锁</td>
</tr>
<tr>
<td>array[tail]&#x3D;e1</td>
<td></td>
<td></td>
</tr>
<tr>
<td></td>
<td>lock.lockInterruptibly()</td>
<td>即使 CPU 切换到线程2，但由于t1已经对该对象上锁，因此线程2卡在这儿进不去</td>
</tr>
<tr>
<td>tail++</td>
<td></td>
<td>切换回线程1 执行后续代码</td>
</tr>
<tr>
<td>lock.unlock()</td>
<td></td>
<td>线程1 解锁</td>
</tr>
<tr>
<td></td>
<td>array[tail]&#x3D;e2</td>
<td>线程2 此时才能获得锁，执行它的代码</td>
</tr>
<tr>
<td></td>
<td>tail++</td>
<td></td>
</tr>
</tbody></table>
<ul>
<li>另一种情况是线程2 先获得锁，线程1 被挡在外面</li>
<li>要明白<strong>保护的本质</strong>，本例中是保护的是 tail 位置读写的安全</li>
</ul>
<p>事情还没有完，上面的例子是队列还没有放满的情况，考虑下面的代码（这回锁同时保护了 tail 和 size 的读写安全）</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="type">ReentrantLock</span> <span class="variable">lock</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">ReentrantLock</span>();</span><br><span class="line"><span class="type">int</span> <span class="variable">size</span> <span class="operator">=</span> <span class="number">0</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">offer</span><span class="params">(String e)</span> &#123;</span><br><span class="line">    lock.lockInterruptibly();</span><br><span class="line">    <span class="keyword">try</span> &#123;</span><br><span class="line">        <span class="keyword">if</span>(isFull()) &#123;</span><br><span class="line">            <span class="comment">// 满了怎么办?</span></span><br><span class="line">        &#125;</span><br><span class="line">        array[tail] = e;</span><br><span class="line">        tail++;</span><br><span class="line">        </span><br><span class="line">        size++;</span><br><span class="line">    &#125; <span class="keyword">finally</span> &#123;</span><br><span class="line">        lock.unlock();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">private</span> <span class="type">boolean</span> <span class="title function_">isFull</span><span class="params">()</span> &#123;</span><br><span class="line">    <span class="keyword">return</span> size == array.length;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>之前是返回 false 表示添加失败，前面分析过想达到这么一种效果：</p>
<ul>
<li>在队列满时，不是立刻返回，而是当前线程进入等待</li>
<li>什么时候队列不满了，再唤醒这个等待的线程，从上次的代码处继续向下运行</li>
</ul>
<p>ReentrantLock 可以配合条件变量来实现，代码进化为</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="type">ReentrantLock</span> <span class="variable">lock</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">ReentrantLock</span>();</span><br><span class="line"><span class="type">Condition</span> <span class="variable">tailWaits</span> <span class="operator">=</span> lock.newCondition(); <span class="comment">// 条件变量</span></span><br><span class="line"><span class="type">int</span> <span class="variable">size</span> <span class="operator">=</span> <span class="number">0</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">offer</span><span class="params">(String e)</span> &#123;</span><br><span class="line">    lock.lockInterruptibly();</span><br><span class="line">    <span class="keyword">try</span> &#123;</span><br><span class="line">        <span class="keyword">while</span> (isFull()) &#123;</span><br><span class="line">            tailWaits.await();	<span class="comment">// 当队列满时, 当前线程进入 tailWaits 等待</span></span><br><span class="line">        &#125;</span><br><span class="line">        array[tail] = e;</span><br><span class="line">        tail++;</span><br><span class="line">        </span><br><span class="line">        size++;</span><br><span class="line">    &#125; <span class="keyword">finally</span> &#123;</span><br><span class="line">        lock.unlock();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">private</span> <span class="type">boolean</span> <span class="title function_">isFull</span><span class="params">()</span> &#123;</span><br><span class="line">    <span class="keyword">return</span> size == array.length;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<ul>
<li>条件变量底层也是个队列，用来存储这些需要等待的线程，当队列满了，就会将 offer 线程加入条件队列，并暂时释放锁</li>
<li>将来我们的队列如果不满了（由 poll 线程那边得知）可以调用 tailWaits.signal() 来唤醒 tailWaits 中首个等待的线程，被唤醒的线程会再次抢到锁，从上次 await 处继续向下运行</li>
</ul>
<p>思考为何要用 while 而不是 if，设队列容量是 3</p>
<table>
<thead>
<tr>
<th>操作前</th>
<th>offer(4)</th>
<th>offer(5)</th>
<th>poll()</th>
<th>操作后</th>
</tr>
</thead>
<tbody><tr>
<td>[1 2 3]</td>
<td>队列满，进入tailWaits 等待</td>
<td></td>
<td></td>
<td>[1 2 3]</td>
</tr>
<tr>
<td>[1 2 3]</td>
<td></td>
<td></td>
<td>取走 1，队列不满，唤醒线程</td>
<td>[2 3]</td>
</tr>
<tr>
<td>[2 3]</td>
<td></td>
<td>抢先获得锁，发现不满，放入 5</td>
<td></td>
<td>[2 3 5]</td>
</tr>
<tr>
<td>[2 3 5]</td>
<td>从上次等待处直接向下执行</td>
<td></td>
<td></td>
<td>[2 3 5 ?]</td>
</tr>
</tbody></table>
<p>关键点：</p>
<ul>
<li>从 tailWaits 中唤醒的线程，会与新来的 offer 的线程争抢锁，谁能抢到是不一定的，如果后者先抢到，就会导致条件又发生变化</li>
<li>这种情况称之为<strong>虚假唤醒</strong>，唤醒后应该重新检查条件，看是不是得重新进入等待</li>
</ul>
<p>最后的实现代码</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 单锁实现</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> &lt;E&gt; 元素类型</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">BlockingQueue1</span>&lt;E&gt; <span class="keyword">implements</span> <span class="title class_">BlockingQueue</span>&lt;E&gt; &#123;</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">final</span> E[] array;</span><br><span class="line">    <span class="keyword">private</span> <span class="type">int</span> <span class="variable">head</span> <span class="operator">=</span> <span class="number">0</span>;</span><br><span class="line">    <span class="keyword">private</span> <span class="type">int</span> <span class="variable">tail</span> <span class="operator">=</span> <span class="number">0</span>;</span><br><span class="line">    <span class="keyword">private</span> <span class="type">int</span> <span class="variable">size</span> <span class="operator">=</span> <span class="number">0</span>; <span class="comment">// 元素个数</span></span><br><span class="line"></span><br><span class="line">    <span class="meta">@SuppressWarnings(&quot;all&quot;)</span></span><br><span class="line">    <span class="keyword">public</span> <span class="title function_">BlockingQueue1</span><span class="params">(<span class="type">int</span> capacity)</span> &#123;</span><br><span class="line">        array = (E[]) <span class="keyword">new</span> <span class="title class_">Object</span>[capacity];</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="type">ReentrantLock</span> <span class="variable">lock</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">ReentrantLock</span>();</span><br><span class="line">    <span class="type">Condition</span> <span class="variable">tailWaits</span> <span class="operator">=</span> lock.newCondition();</span><br><span class="line">    <span class="type">Condition</span> <span class="variable">headWaits</span> <span class="operator">=</span> lock.newCondition();</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">offer</span><span class="params">(E e)</span> <span class="keyword">throws</span> InterruptedException &#123;</span><br><span class="line">        lock.lockInterruptibly();</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            <span class="keyword">while</span> (isFull()) &#123;</span><br><span class="line">                tailWaits.await();</span><br><span class="line">            &#125;</span><br><span class="line">            array[tail] = e;</span><br><span class="line">            <span class="keyword">if</span> (++tail == array.length) &#123;</span><br><span class="line">                tail = <span class="number">0</span>;</span><br><span class="line">            &#125;</span><br><span class="line">            size++;</span><br><span class="line">            headWaits.signal();</span><br><span class="line">        &#125; <span class="keyword">finally</span> &#123;</span><br><span class="line">            lock.unlock();</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">offer</span><span class="params">(E e, <span class="type">long</span> timeout)</span> <span class="keyword">throws</span> InterruptedException &#123;</span><br><span class="line">        lock.lockInterruptibly();</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            <span class="type">long</span> <span class="variable">t</span> <span class="operator">=</span> TimeUnit.MILLISECONDS.toNanos(timeout);</span><br><span class="line">            <span class="keyword">while</span> (isFull()) &#123;</span><br><span class="line">                <span class="keyword">if</span> (t &lt;= <span class="number">0</span>) &#123;</span><br><span class="line">                    <span class="keyword">return</span>;</span><br><span class="line">                &#125;</span><br><span class="line">                t = tailWaits.awaitNanos(t);</span><br><span class="line">            &#125;</span><br><span class="line">            array[tail] = e;</span><br><span class="line">            <span class="keyword">if</span> (++tail == array.length) &#123;</span><br><span class="line">                tail = <span class="number">0</span>;</span><br><span class="line">            &#125;</span><br><span class="line">            size++;</span><br><span class="line">            headWaits.signal();</span><br><span class="line">        &#125; <span class="keyword">finally</span> &#123;</span><br><span class="line">            lock.unlock();</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> E <span class="title function_">poll</span><span class="params">()</span> <span class="keyword">throws</span> InterruptedException &#123;</span><br><span class="line">        lock.lockInterruptibly();</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            <span class="keyword">while</span> (isEmpty()) &#123;</span><br><span class="line">                headWaits.await();</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="type">E</span> <span class="variable">e</span> <span class="operator">=</span> array[head];</span><br><span class="line">            array[head] = <span class="literal">null</span>; <span class="comment">// help GC</span></span><br><span class="line">            <span class="keyword">if</span> (++head == array.length) &#123;</span><br><span class="line">                head = <span class="number">0</span>;</span><br><span class="line">            &#125;</span><br><span class="line">            size--;</span><br><span class="line">            tailWaits.signal();</span><br><span class="line">            <span class="keyword">return</span> e;</span><br><span class="line">        &#125; <span class="keyword">finally</span> &#123;</span><br><span class="line">            lock.unlock();</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> <span class="type">boolean</span> <span class="title function_">isEmpty</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> size == <span class="number">0</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> <span class="type">boolean</span> <span class="title function_">isFull</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> size == array.length;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<ul>
<li>public void offer(E e, long timeout) throws InterruptedException 是带超时的版本，可以只等待一段时间，而不是永久等下去，类似的 poll 也可以做带超时的版本，这个留给大家了</li>
</ul>
<blockquote>
<p><strong>注意</strong></p>
<ul>
<li>JDK 中 BlockingQueue 接口的方法命名与我的示例有些差异<ul>
<li>方法 offer(E e) 是非阻塞的实现，阻塞实现方法为 put(E e)</li>
<li>方法 poll() 是非阻塞的实现，阻塞实现方法为 take()</li>
</ul>
</li>
</ul>
</blockquote>
<h3 id="双锁实现"><a href="#双锁实现" class="headerlink" title="双锁实现"></a>双锁实现</h3><p>单锁的缺点在于：</p>
<ul>
<li>生产和消费几乎是不冲突的，唯一冲突的是生产者和消费者它们有可能同时修改 size</li>
<li>冲突的主要是生产者之间：多个 offer 线程修改 tail</li>
<li>冲突的还有消费者之间：多个 poll 线程修改 head</li>
</ul>
<p>如果希望进一步提高性能，可以用两把锁</p>
<ul>
<li>一把锁保护 tail</li>
<li>另一把锁保护 head</li>
</ul>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="type">ReentrantLock</span> <span class="variable">headLock</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">ReentrantLock</span>();  <span class="comment">// 保护 head 的锁</span></span><br><span class="line"><span class="type">Condition</span> <span class="variable">headWaits</span> <span class="operator">=</span> headLock.newCondition(); <span class="comment">// 队列空时，需要等待的线程集合</span></span><br><span class="line"></span><br><span class="line"><span class="type">ReentrantLock</span> <span class="variable">tailLock</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">ReentrantLock</span>();  <span class="comment">// 保护 tail 的锁</span></span><br><span class="line"><span class="type">Condition</span> <span class="variable">tailWaits</span> <span class="operator">=</span> tailLock.newCondition(); <span class="comment">// 队列满时，需要等待的线程集合</span></span><br></pre></td></tr></table></figure>

<p>先看看 offer 方法的初步实现</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="meta">@Override</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">offer</span><span class="params">(E e)</span> <span class="keyword">throws</span> InterruptedException &#123;</span><br><span class="line">    tailLock.lockInterruptibly();</span><br><span class="line">    <span class="keyword">try</span> &#123;</span><br><span class="line">        <span class="comment">// 队列满等待</span></span><br><span class="line">        <span class="keyword">while</span> (isFull()) &#123;</span><br><span class="line">            tailWaits.await();</span><br><span class="line">        &#125;</span><br><span class="line">        </span><br><span class="line">        <span class="comment">// 不满则入队</span></span><br><span class="line">        array[tail] = e;</span><br><span class="line">        <span class="keyword">if</span> (++tail == array.length) &#123;</span><br><span class="line">            tail = <span class="number">0</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        </span><br><span class="line">        <span class="comment">// 修改 size （有问题）</span></span><br><span class="line">        size++;</span><br><span class="line">        </span><br><span class="line">    &#125; <span class="keyword">finally</span> &#123;</span><br><span class="line">        tailLock.unlock();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>上面代码的缺点是 size 并不受 tailLock 保护，tailLock 与 headLock 是两把不同的锁，并不能实现互斥的效果。因此，size 需要用下面的代码保证原子性</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="type">AtomicInteger</span> <span class="variable">size</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">AtomicInteger</span>(<span class="number">0</span>);	   <span class="comment">// 保护 size 的原子变量</span></span><br><span class="line"></span><br><span class="line">size.getAndIncrement(); <span class="comment">// 自增</span></span><br><span class="line">size.getAndDecrement(); <span class="comment">// 自减</span></span><br></pre></td></tr></table></figure>

<p>代码修改为</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="meta">@Override</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">offer</span><span class="params">(E e)</span> <span class="keyword">throws</span> InterruptedException &#123;</span><br><span class="line">    tailLock.lockInterruptibly();</span><br><span class="line">    <span class="keyword">try</span> &#123;</span><br><span class="line">        <span class="comment">// 队列满等待</span></span><br><span class="line">        <span class="keyword">while</span> (isFull()) &#123;</span><br><span class="line">            tailWaits.await();</span><br><span class="line">        &#125;</span><br><span class="line">        </span><br><span class="line">        <span class="comment">// 不满则入队</span></span><br><span class="line">        array[tail] = e;</span><br><span class="line">        <span class="keyword">if</span> (++tail == array.length) &#123;</span><br><span class="line">            tail = <span class="number">0</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        </span><br><span class="line">        <span class="comment">// 修改 size</span></span><br><span class="line">        size.getAndIncrement();</span><br><span class="line">        </span><br><span class="line">    &#125; <span class="keyword">finally</span> &#123;</span><br><span class="line">        tailLock.unlock();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>对称地，可以写出 poll 方法</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="meta">@Override</span></span><br><span class="line"><span class="keyword">public</span> E <span class="title function_">poll</span><span class="params">()</span> <span class="keyword">throws</span> InterruptedException &#123;</span><br><span class="line">    E e;</span><br><span class="line">    headLock.lockInterruptibly();</span><br><span class="line">    <span class="keyword">try</span> &#123;</span><br><span class="line">        <span class="comment">// 队列空等待</span></span><br><span class="line">        <span class="keyword">while</span> (isEmpty()) &#123;</span><br><span class="line">            headWaits.await();</span><br><span class="line">        &#125;</span><br><span class="line">        </span><br><span class="line">        <span class="comment">// 不空则出队</span></span><br><span class="line">        e = array[head];</span><br><span class="line">        <span class="keyword">if</span> (++head == array.length) &#123;</span><br><span class="line">            head = <span class="number">0</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        </span><br><span class="line">        <span class="comment">// 修改 size</span></span><br><span class="line">        size.getAndDecrement();</span><br><span class="line">        </span><br><span class="line">    &#125; <span class="keyword">finally</span> &#123;</span><br><span class="line">        headLock.unlock();</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> e;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>下面来看一个难题，就是如何通知 headWaits 和 tailWaits 中等待的线程，比如 poll 方法拿走一个元素，通知 tailWaits：我拿走一个，不满了噢，你们可以放了，因此代码改为</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="meta">@Override</span></span><br><span class="line"><span class="keyword">public</span> E <span class="title function_">poll</span><span class="params">()</span> <span class="keyword">throws</span> InterruptedException &#123;</span><br><span class="line">    E e;</span><br><span class="line">    headLock.lockInterruptibly();</span><br><span class="line">    <span class="keyword">try</span> &#123;</span><br><span class="line">        <span class="comment">// 队列空等待</span></span><br><span class="line">        <span class="keyword">while</span> (isEmpty()) &#123;</span><br><span class="line">            headWaits.await();</span><br><span class="line">        &#125;</span><br><span class="line">        </span><br><span class="line">        <span class="comment">// 不空则出队</span></span><br><span class="line">        e = array[head];</span><br><span class="line">        <span class="keyword">if</span> (++head == array.length) &#123;</span><br><span class="line">            head = <span class="number">0</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        </span><br><span class="line">        <span class="comment">// 修改 size</span></span><br><span class="line">        size.getAndDecrement();</span><br><span class="line">        </span><br><span class="line">        <span class="comment">// 通知 tailWaits 不满（有问题）</span></span><br><span class="line">        tailWaits.signal();</span><br><span class="line">        </span><br><span class="line">    &#125; <span class="keyword">finally</span> &#123;</span><br><span class="line">        headLock.unlock();</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> e;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>问题在于要使用这些条件变量的 await()， signal() 等方法需要先获得与之关联的锁，上面的代码若直接运行会出现以下错误</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line">java.lang.IllegalMonitorStateException</span><br></pre></td></tr></table></figure>

<p>那有同学说，加上锁不就行了吗，于是写出了下面的代码</p>
<p><img src= "" data-lazy-src="http://rx6zk4j2b.hn-bkt.clouddn.com/blogs/image-20230208160343493.png" alt="image-20230208160343493"></p>
<p>发现什么问题了？两把锁这么嵌套使用，非常容易出现死锁，如下所示</p>
<p><img src= "" data-lazy-src="http://rx6zk4j2b.hn-bkt.clouddn.com/blogs/image-20230208160143386.png" alt="image-20230208160143386"></p>
<p>因此得避免嵌套，两段加锁的代码变成了下面平级的样子</p>
<p><img src= "" data-lazy-src="http://rx6zk4j2b.hn-bkt.clouddn.com/blogs/image-20230208162857435.png" alt="image-20230208162857435"></p>
<p>性能还可以进一步提升</p>
<ol>
<li><p>代码调整后 offer 并没有同时获取 tailLock 和 headLock 两把锁，因此两次加锁之间会有<strong>空隙</strong>，这个空隙内可能有其它的 offer 线程添加了更多的元素，那么这些线程都要执行 signal()，通知 poll 线程队列非空吗？</p>
<ul>
<li>每次调用 signal() 都需要这些 offer 线程先获得 headLock 锁，成本较高，要想法减少 offer 线程获得 headLock 锁的次数</li>
<li>可以加一个条件：当 offer 增加前队列为空，即从 0 变化到不空，才由此 offer 线程来通知 headWaits，其它情况不归它管</li>
</ul>
</li>
<li><p>队列从 0 变化到不空，会唤醒一个等待的 poll 线程，这个线程被唤醒后，肯定能拿到 headLock 锁，因此它具备了唤醒 headWaits 上其它 poll 线程的先决条件。如果检查出此时有其它 offer 线程新增了元素（不空，但不是从0变化而来），那么不妨由此 poll 线程来唤醒其它 poll 线程</p>
</li>
</ol>
<p>这个技巧被称之为级联通知（cascading notifies），类似的原因</p>
<ol start="3">
<li>在 poll 时队列从满变化到不满，才由此 poll 线程来唤醒一个等待的 offer 线程，目的也是为了减少 poll 线程对 tailLock 上锁次数，剩下等待的 offer 线程由这个 offer 线程间接唤醒</li>
</ol>
<p>最终的代码为</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">BlockingQueue2</span>&lt;E&gt; <span class="keyword">implements</span> <span class="title class_">BlockingQueue</span>&lt;E&gt; &#123;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">final</span> E[] array;</span><br><span class="line">    <span class="keyword">private</span> <span class="type">int</span> <span class="variable">head</span> <span class="operator">=</span> <span class="number">0</span>;</span><br><span class="line">    <span class="keyword">private</span> <span class="type">int</span> <span class="variable">tail</span> <span class="operator">=</span> <span class="number">0</span>;</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">final</span> <span class="type">AtomicInteger</span> <span class="variable">size</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">AtomicInteger</span>(<span class="number">0</span>);</span><br><span class="line">    <span class="type">ReentrantLock</span> <span class="variable">headLock</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">ReentrantLock</span>();</span><br><span class="line">    <span class="type">Condition</span> <span class="variable">headWaits</span> <span class="operator">=</span> headLock.newCondition();</span><br><span class="line">    <span class="type">ReentrantLock</span> <span class="variable">tailLock</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">ReentrantLock</span>();</span><br><span class="line">    <span class="type">Condition</span> <span class="variable">tailWaits</span> <span class="operator">=</span> tailLock.newCondition();</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="title function_">BlockingQueue2</span><span class="params">(<span class="type">int</span> capacity)</span> &#123;</span><br><span class="line">        <span class="built_in">this</span>.array = (E[]) <span class="keyword">new</span> <span class="title class_">Object</span>[capacity];</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">offer</span><span class="params">(E e)</span> <span class="keyword">throws</span> InterruptedException &#123;</span><br><span class="line">        <span class="type">int</span> c;</span><br><span class="line">        tailLock.lockInterruptibly();</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            <span class="keyword">while</span> (isFull()) &#123;</span><br><span class="line">                tailWaits.await();</span><br><span class="line">            &#125;</span><br><span class="line">            array[tail] = e;</span><br><span class="line">            <span class="keyword">if</span> (++tail == array.length) &#123;</span><br><span class="line">                tail = <span class="number">0</span>;</span><br><span class="line">            &#125;            </span><br><span class="line">            c = size.getAndIncrement();</span><br><span class="line">            <span class="comment">// a. 队列不满, 但不是从满-&gt;不满, 由此offer线程唤醒其它offer线程</span></span><br><span class="line">            <span class="keyword">if</span> (c + <span class="number">1</span> &lt; array.length) &#123;</span><br><span class="line">                tailWaits.signal();</span><br><span class="line">            &#125;</span><br><span class="line">        &#125; <span class="keyword">finally</span> &#123;</span><br><span class="line">            tailLock.unlock();</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="comment">// b. 从0-&gt;不空, 由此offer线程唤醒等待的poll线程</span></span><br><span class="line">        <span class="keyword">if</span> (c == <span class="number">0</span>) &#123;</span><br><span class="line">            headLock.lock();</span><br><span class="line">            <span class="keyword">try</span> &#123;</span><br><span class="line">                headWaits.signal();</span><br><span class="line">            &#125; <span class="keyword">finally</span> &#123;</span><br><span class="line">                headLock.unlock();</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> E <span class="title function_">poll</span><span class="params">()</span> <span class="keyword">throws</span> InterruptedException &#123;</span><br><span class="line">        E e;</span><br><span class="line">        <span class="type">int</span> c;</span><br><span class="line">        headLock.lockInterruptibly();</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            <span class="keyword">while</span> (isEmpty()) &#123;</span><br><span class="line">                headWaits.await(); </span><br><span class="line">            &#125;</span><br><span class="line">            e = array[head]; </span><br><span class="line">            <span class="keyword">if</span> (++head == array.length) &#123;</span><br><span class="line">                head = <span class="number">0</span>;</span><br><span class="line">            &#125;</span><br><span class="line">            c = size.getAndDecrement();</span><br><span class="line">            <span class="comment">// b. 队列不空, 但不是从0变化到不空，由此poll线程通知其它poll线程</span></span><br><span class="line">            <span class="keyword">if</span> (c &gt; <span class="number">1</span>) &#123;</span><br><span class="line">                headWaits.signal();</span><br><span class="line">            &#125;</span><br><span class="line">        &#125; <span class="keyword">finally</span> &#123;</span><br><span class="line">            headLock.unlock();</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="comment">// a. 从满-&gt;不满, 由此poll线程唤醒等待的offer线程</span></span><br><span class="line">        <span class="keyword">if</span> (c == array.length) &#123;</span><br><span class="line">            tailLock.lock();</span><br><span class="line">            <span class="keyword">try</span> &#123;</span><br><span class="line">                tailWaits.signal();</span><br><span class="line">            &#125; <span class="keyword">finally</span> &#123;</span><br><span class="line">                tailLock.unlock();</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> e;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> <span class="type">boolean</span> <span class="title function_">isEmpty</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> size.get() == <span class="number">0</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> <span class="type">boolean</span> <span class="title function_">isFull</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> size.get() == array.length;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>双锁实现的非常精巧，据说作者 Doug Lea 花了一年的时间才完善了此段代码</p>
<h2 id="2-9-堆"><a href="#2-9-堆" class="headerlink" title="2.9 堆"></a>2.9 堆</h2><p>以大顶堆为例，相对于之前的优先级队列，增加了堆化等方法</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">MaxHeap</span> &#123;</span><br><span class="line">    <span class="type">int</span>[] array;</span><br><span class="line">    <span class="type">int</span> size;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="title function_">MaxHeap</span><span class="params">(<span class="type">int</span> capacity)</span> &#123;</span><br><span class="line">        <span class="built_in">this</span>.array = <span class="keyword">new</span> <span class="title class_">int</span>[capacity];</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 获取堆顶元素</span></span><br><span class="line"><span class="comment">     *</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@return</span> 堆顶元素</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">public</span> <span class="type">int</span> <span class="title function_">peek</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> array[<span class="number">0</span>];</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 删除堆顶元素</span></span><br><span class="line"><span class="comment">     *</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@return</span> 堆顶元素</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">public</span> <span class="type">int</span> <span class="title function_">poll</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="type">int</span> <span class="variable">top</span> <span class="operator">=</span> array[<span class="number">0</span>];</span><br><span class="line">        swap(<span class="number">0</span>, size - <span class="number">1</span>);</span><br><span class="line">        size--;</span><br><span class="line">        down(<span class="number">0</span>);</span><br><span class="line">        <span class="keyword">return</span> top;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 删除指定索引处元素</span></span><br><span class="line"><span class="comment">     *</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> index 索引</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@return</span> 被删除元素</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">public</span> <span class="type">int</span> <span class="title function_">poll</span><span class="params">(<span class="type">int</span> index)</span> &#123;</span><br><span class="line">        <span class="type">int</span> <span class="variable">deleted</span> <span class="operator">=</span> array[index];</span><br><span class="line">        swap(index, size - <span class="number">1</span>);</span><br><span class="line">        size--;</span><br><span class="line">        down(index);</span><br><span class="line">        <span class="keyword">return</span> deleted;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 替换堆顶元素</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> replaced 新元素</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">replace</span><span class="params">(<span class="type">int</span> replaced)</span> &#123;</span><br><span class="line">        array[<span class="number">0</span>] = replaced;</span><br><span class="line">        down(<span class="number">0</span>);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 堆的尾部添加元素</span></span><br><span class="line"><span class="comment">     *</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> offered 新元素</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@return</span> 是否添加成功</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">public</span> <span class="type">boolean</span> <span class="title function_">offer</span><span class="params">(<span class="type">int</span> offered)</span> &#123;</span><br><span class="line">        <span class="keyword">if</span> (size == array.length) &#123;</span><br><span class="line">            <span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        up(offered);</span><br><span class="line">        size++;</span><br><span class="line">        <span class="keyword">return</span> <span class="literal">true</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 将 offered 元素上浮: 直至 offered 小于父元素或到堆顶</span></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">void</span> <span class="title function_">up</span><span class="params">(<span class="type">int</span> offered)</span> &#123;</span><br><span class="line">        <span class="type">int</span> <span class="variable">child</span> <span class="operator">=</span> size;</span><br><span class="line">        <span class="keyword">while</span> (child &gt; <span class="number">0</span>) &#123;</span><br><span class="line">            <span class="type">int</span> <span class="variable">parent</span> <span class="operator">=</span> (child - <span class="number">1</span>) / <span class="number">2</span>;</span><br><span class="line">            <span class="keyword">if</span> (offered &gt; array[parent]) &#123;</span><br><span class="line">                array[child] = array[parent];</span><br><span class="line">            &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">                <span class="keyword">break</span>;</span><br><span class="line">            &#125;</span><br><span class="line">            child = parent;</span><br><span class="line">        &#125;</span><br><span class="line">        array[child] = offered;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="title function_">MaxHeap</span><span class="params">(<span class="type">int</span>[] array)</span> &#123;</span><br><span class="line">        <span class="built_in">this</span>.array = array;</span><br><span class="line">        <span class="built_in">this</span>.size = array.length;</span><br><span class="line">        heapify();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 建堆</span></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">void</span> <span class="title function_">heapify</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="comment">// 如何找到最后这个非叶子节点  size / 2 - 1</span></span><br><span class="line">        <span class="keyword">for</span> (<span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> size / <span class="number">2</span> - <span class="number">1</span>; i &gt;= <span class="number">0</span>; i--) &#123;</span><br><span class="line">            down(i);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 将 parent 索引处的元素下潜: 与两个孩子较大者交换, 直至没孩子或孩子没它大</span></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">void</span> <span class="title function_">down</span><span class="params">(<span class="type">int</span> parent)</span> &#123;</span><br><span class="line">        <span class="type">int</span> <span class="variable">left</span> <span class="operator">=</span> parent * <span class="number">2</span> + <span class="number">1</span>;</span><br><span class="line">        <span class="type">int</span> <span class="variable">right</span> <span class="operator">=</span> left + <span class="number">1</span>;</span><br><span class="line">        <span class="type">int</span> <span class="variable">max</span> <span class="operator">=</span> parent;</span><br><span class="line">        <span class="keyword">if</span> (left &lt; size &amp;&amp; array[left] &gt; array[max]) &#123;</span><br><span class="line">            max = left;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">if</span> (right &lt; size &amp;&amp; array[right] &gt; array[max]) &#123;</span><br><span class="line">            max = right;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">if</span> (max != parent) &#123; <span class="comment">// 找到了更大的孩子</span></span><br><span class="line">            swap(max, parent);</span><br><span class="line">            down(max);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 交换两个索引处的元素</span></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">void</span> <span class="title function_">swap</span><span class="params">(<span class="type">int</span> i, <span class="type">int</span> j)</span> &#123;</span><br><span class="line">        <span class="type">int</span> <span class="variable">t</span> <span class="operator">=</span> array[i];</span><br><span class="line">        array[i] = array[j];</span><br><span class="line">        array[j] = t;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> &#123;</span><br><span class="line"></span><br><span class="line">        <span class="type">int</span>[] array = &#123;<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>, <span class="number">4</span>, <span class="number">5</span>, <span class="number">6</span>, <span class="number">7</span>&#125;;</span><br><span class="line">        <span class="type">MaxHeap</span> <span class="variable">maxHeap</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">MaxHeap</span>(array);</span><br><span class="line">        System.out.println(Arrays.toString(maxHeap.array));</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>



<p><strong>建堆</strong></p>
<p>Floyd 建堆算法作者（也是之前龟兔赛跑判环作者）：</p>
<p><img src= "" data-lazy-src="http://rx6zk4j2b.hn-bkt.clouddn.com/blogs/image-20230213095110902.png" alt="image-20230213095110902"></p>
<ol>
<li>找到最后一个非叶子节点</li>
<li>从后向前，对每个节点执行下潜</li>
</ol>
<p>一些规律</p>
<ul>
<li>一棵满二叉树节点个数为 $2^h-1$，如下例中高度 $h&#x3D;3$ 节点数是 $2^3-1&#x3D;7$</li>
<li>非叶子节点范围为 $[0, size&#x2F;2-1]$</li>
</ul>
<p>算法时间复杂度分析</p>
<p><img src= "" data-lazy-src="http://rx6zk4j2b.hn-bkt.clouddn.com/blogs/image-20230213114024607.png" alt="image-20230213114024607"></p>
<p>下面看交换次数的推导：设节点高度为 3</p>
<table>
<thead>
<tr>
<th></th>
<th>本层节点数</th>
<th>高度</th>
<th>下潜最多交换次数（高度-1）</th>
</tr>
</thead>
<tbody><tr>
<td>4567 这层</td>
<td>4</td>
<td>1</td>
<td>0</td>
</tr>
<tr>
<td>23这层</td>
<td>2</td>
<td>2</td>
<td>1</td>
</tr>
<tr>
<td>1这层</td>
<td>1</td>
<td>3</td>
<td>2</td>
</tr>
</tbody></table>
<p>每一层的交换次数为：$节点个数*此节点交换次数$，总的交换次数为<br>$$<br>\begin{aligned}<br>&amp; 4 * 0 + 2 * 1 + 1 * 2 \</p>
<p>&amp; \frac{8}{2}*0 + \frac{8}{4}*1 + \frac{8}{8}*2 \</p>
<p>&amp; \frac{8}{2^1}*0 + \frac{8}{2^2}*1 + \frac{8}{2^3}*2\</p>
<p>\end{aligned}<br>$$<br>即<br>$$<br>\sum_{i&#x3D;1}^{h}(\frac{2^h}{2^i}*(i-1))<br>$$<br>在 <a target="_blank" rel="noopener" href="https://www.wolframalpha.com/">https://www.wolframalpha.com/</a> 输入</p>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">Sum[\(40)Divide[Power[2,x],Power[2,i]]*\(40)i-1\(41)\(41),&#123;i,1,x&#125;]</span><br></pre></td></tr></table></figure>

<p>推导出<br>$$<br>2^h -h -1<br>$$<br>其中 $2^h \approx n$，$h \approx \log_2{n}$，因此有时间复杂度 $O(n)$</p>
<h2 id="2-10-二叉树"><a href="#2-10-二叉树" class="headerlink" title="2.10 二叉树"></a>2.10 二叉树</h2><p>二叉树是这么一种树状结构：每个节点最多有两个孩子，左孩子和右孩子</p>
<p>重要的二叉树结构</p>
<ul>
<li>完全二叉树（complete binary tree）是一种二叉树结构，除最后一层以外，每一层都必须填满，填充时要遵从先左后右</li>
<li>平衡二叉树（balance binary tree）是一种二叉树结构，其中每个节点的左右子树高度相差不超过 1</li>
</ul>
<h3 id="存储"><a href="#存储" class="headerlink" title="存储"></a>存储</h3><p>存储方式分为两种</p>
<ol>
<li>定义树节点与左、右孩子引用（TreeNode）</li>
<li>使用数组，前面讲堆时用过，若以 0 作为树的根，索引可以通过如下方式计算<ul>
<li>父 &#x3D; floor((子 - 1) &#x2F; 2)</li>
<li>左孩子 &#x3D; 父 * 2 + 1</li>
<li>右孩子 &#x3D; 父 * 2 + 2</li>
</ul>
</li>
</ol>
<h3 id="遍历"><a href="#遍历" class="headerlink" title="遍历"></a>遍历</h3><p>遍历也分为两种</p>
<ol>
<li>广度优先遍历（Breadth-first order）：尽可能先访问距离根最近的节点，也称为层序遍历</li>
<li>深度优先遍历（Depth-first order）：对于二叉树，可以进一步分成三种（要深入到叶子节点）<ol>
<li>pre-order 前序遍历，对于每一棵子树，先访问该节点，然后是左子树，最后是右子树</li>
<li>in-order 中序遍历，对于每一棵子树，先访问左子树，然后是该节点，最后是右子树</li>
<li>post-order 后序遍历，对于每一棵子树，先访问左子树，然后是右子树，最后是该节点</li>
</ol>
</li>
</ol>
<h4 id="广度优先"><a href="#广度优先" class="headerlink" title="广度优先"></a>广度优先</h4><p><img src= "" data-lazy-src="http://rx6zk4j2b.hn-bkt.clouddn.com/blogs/image-20230216153607396.png" alt="image-20230216153607396"></p>
<table>
<thead>
<tr>
<th>本轮开始时队列</th>
<th>本轮访问节点</th>
</tr>
</thead>
<tbody><tr>
<td>[1]</td>
<td>1</td>
</tr>
<tr>
<td>[2, 3]</td>
<td>2</td>
</tr>
<tr>
<td>[3, 4]</td>
<td>3</td>
</tr>
<tr>
<td>[4, 5, 6]</td>
<td>4</td>
</tr>
<tr>
<td>[5, 6]</td>
<td>5</td>
</tr>
<tr>
<td>[6, 7, 8]</td>
<td>6</td>
</tr>
<tr>
<td>[7, 8]</td>
<td>7</td>
</tr>
<tr>
<td>[8]</td>
<td>8</td>
</tr>
<tr>
<td>[]</td>
<td></td>
</tr>
</tbody></table>
<ol>
<li>初始化，将根节点加入队列</li>
<li>循环处理队列中每个节点，直至队列为空</li>
<li>每次循环内处理节点后，将它的孩子节点（即下一层的节点）加入队列</li>
</ol>
<blockquote>
<p>注意</p>
<ul>
<li><p>以上用队列来层序遍历是针对  TreeNode 这种方式表示的二叉树</p>
</li>
<li><p>对于数组表现的二叉树，则直接遍历数组即可，自然为层序遍历的顺序</p>
</li>
</ul>
</blockquote>
<h4 id="深度优先"><a href="#深度优先" class="headerlink" title="深度优先"></a>深度优先</h4><p><img src= "" data-lazy-src="http://rx6zk4j2b.hn-bkt.clouddn.com/blogs/image-20230221110443230.png" alt="image-20230221110443230"></p>
<table>
<thead>
<tr>
<th>栈暂存</th>
<th>已处理</th>
<th>前序遍历</th>
<th>中序遍历</th>
</tr>
</thead>
<tbody><tr>
<td>[1]</td>
<td>1 ✔️ 左💤 右💤</td>
<td>1</td>
<td></td>
</tr>
<tr>
<td>[1, 2]</td>
<td>2✔️ 左💤 右💤<br />1✔️ 左💤 右💤</td>
<td>2</td>
<td></td>
</tr>
<tr>
<td>[1, 2, 4]</td>
<td>4✔️ 左✔️ 右✔️<br />2✔️ 左💤 右💤<br />1✔️ 左💤 右💤</td>
<td>4</td>
<td>4</td>
</tr>
<tr>
<td>[1, 2]</td>
<td>2✔️ 左✔️ 右✔️<br />1✔️ 左💤 右💤</td>
<td></td>
<td>2</td>
</tr>
<tr>
<td>[1]</td>
<td>1✔️ 左✔️ 右💤</td>
<td></td>
<td>1</td>
</tr>
<tr>
<td>[1, 3]</td>
<td>3✔️ 左💤 右💤<br />1✔️ 左✔️ 右💤</td>
<td>3</td>
<td></td>
</tr>
<tr>
<td>[1, 3, 5]</td>
<td>5✔️ 左✔️ 右✔️<br />3✔️ 左💤 右💤<br />1✔️ 左✔️ 右💤</td>
<td>5</td>
<td>5</td>
</tr>
<tr>
<td>[1, 3]</td>
<td>3✔️ 左✔️ 右💤<br />1✔️ 左✔️ 右💤</td>
<td></td>
<td>3</td>
</tr>
<tr>
<td>[1, 3, 6]</td>
<td>6✔️ 左✔️ 右✔️<br />3✔️ 左✔️ 右💤<br />1✔️ 左✔️ 右💤</td>
<td>6</td>
<td>6</td>
</tr>
<tr>
<td>[1, 3]</td>
<td>3✔️ 左✔️ 右✔️<br />1✔️ 左✔️ 右💤</td>
<td></td>
<td></td>
</tr>
<tr>
<td>[1]</td>
<td>1✔️ 左✔️ 右✔️</td>
<td></td>
<td></td>
</tr>
<tr>
<td>[]</td>
<td></td>
<td></td>
<td></td>
</tr>
</tbody></table>
<h4 id="递归实现"><a href="#递归实现" class="headerlink" title="递归实现"></a>递归实现</h4><figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * &lt;h3&gt;前序遍历&lt;/h3&gt;</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> node 节点</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">preOrder</span><span class="params">(TreeNode node)</span> &#123;</span><br><span class="line">    <span class="keyword">if</span> (node == <span class="literal">null</span>) &#123;</span><br><span class="line">        <span class="keyword">return</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    System.out.print(node.val + <span class="string">&quot;\t&quot;</span>); <span class="comment">// 值</span></span><br><span class="line">    preOrder(node.left); <span class="comment">// 左</span></span><br><span class="line">    preOrder(node.right); <span class="comment">// 右</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * &lt;h3&gt;中序遍历&lt;/h3&gt;</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> node 节点</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">inOrder</span><span class="params">(TreeNode node)</span> &#123;</span><br><span class="line">    <span class="keyword">if</span> (node == <span class="literal">null</span>) &#123;</span><br><span class="line">        <span class="keyword">return</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    inOrder(node.left); <span class="comment">// 左</span></span><br><span class="line">    System.out.print(node.val + <span class="string">&quot;\t&quot;</span>); <span class="comment">// 值</span></span><br><span class="line">    inOrder(node.right); <span class="comment">// 右</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * &lt;h3&gt;后序遍历&lt;/h3&gt;</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> node 节点</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">postOrder</span><span class="params">(TreeNode node)</span> &#123;</span><br><span class="line">    <span class="keyword">if</span> (node == <span class="literal">null</span>) &#123;</span><br><span class="line">        <span class="keyword">return</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    postOrder(node.left); <span class="comment">// 左</span></span><br><span class="line">    postOrder(node.right); <span class="comment">// 右</span></span><br><span class="line">    System.out.print(node.val + <span class="string">&quot;\t&quot;</span>); <span class="comment">// 值</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>



<h4 id="非递归实现"><a href="#非递归实现" class="headerlink" title="非递归实现"></a>非递归实现</h4><p><strong>前序遍历</strong></p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line">LinkedListStack&lt;TreeNode&gt; stack = <span class="keyword">new</span> <span class="title class_">LinkedListStack</span>&lt;&gt;();</span><br><span class="line"><span class="type">TreeNode</span> <span class="variable">curr</span> <span class="operator">=</span> root;</span><br><span class="line"></span><br><span class="line"><span class="keyword">while</span> (!stack.isEmpty() || curr != <span class="literal">null</span>) &#123;</span><br><span class="line">    <span class="keyword">if</span> (curr != <span class="literal">null</span>) &#123;</span><br><span class="line">        stack.push(curr);</span><br><span class="line">        System.out.println(curr);</span><br><span class="line">        curr = curr.left;</span><br><span class="line">    &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">        <span class="type">TreeNode</span> <span class="variable">pop</span> <span class="operator">=</span> stack.pop();</span><br><span class="line">        curr = pop.right;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p><strong>中序遍历</strong></p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line">LinkedListStack&lt;TreeNode&gt; stack = <span class="keyword">new</span> <span class="title class_">LinkedListStack</span>&lt;&gt;();</span><br><span class="line"><span class="type">TreeNode</span> <span class="variable">curr</span> <span class="operator">=</span> root;</span><br><span class="line"></span><br><span class="line"><span class="keyword">while</span> (!stack.isEmpty() || curr != <span class="literal">null</span>) &#123;</span><br><span class="line">    <span class="keyword">if</span> (curr != <span class="literal">null</span>) &#123;</span><br><span class="line">        stack.push(curr);</span><br><span class="line">        curr = curr.left;</span><br><span class="line">    &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">        <span class="type">TreeNode</span> <span class="variable">pop</span> <span class="operator">=</span> stack.pop();</span><br><span class="line">        System.out.println(pop);</span><br><span class="line">        curr = pop.right;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p><strong>后序遍历</strong></p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line">LinkedListStack&lt;TreeNode&gt; stack = <span class="keyword">new</span> <span class="title class_">LinkedListStack</span>&lt;&gt;();</span><br><span class="line"><span class="type">TreeNode</span> <span class="variable">curr</span> <span class="operator">=</span> root;</span><br><span class="line"><span class="type">TreeNode</span> <span class="variable">pop</span> <span class="operator">=</span> <span class="literal">null</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">while</span> (!stack.isEmpty() || curr != <span class="literal">null</span>) &#123;</span><br><span class="line">    <span class="keyword">if</span> (curr != <span class="literal">null</span>) &#123;</span><br><span class="line">        stack.push(curr);</span><br><span class="line">        curr = curr.left;</span><br><span class="line">    &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">        <span class="type">TreeNode</span> <span class="variable">peek</span> <span class="operator">=</span> stack.peek();</span><br><span class="line">        <span class="keyword">if</span> (peek.right == <span class="literal">null</span> || peek.right == pop) &#123;</span><br><span class="line">            pop = stack.pop();</span><br><span class="line">            System.out.println(pop);</span><br><span class="line">        &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">            curr = peek.right;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>对于后序遍历，向回走时，需要处理完右子树才能 pop 出栈。如何知道右子树处理完成呢？</p>
<ul>
<li><p>如果栈顶元素的 $right \equiv null$ 表示没啥可处理的，可以出栈</p>
</li>
<li><p>如果栈顶元素的 $right \neq null$，</p>
<ul>
<li>那么使用 lastPop 记录最近出栈的节点，即表示从这个节点向回走</li>
<li>如果栈顶元素的 $right&#x3D;&#x3D;lastPop$ 此时应当出栈</li>
</ul>
</li>
</ul>
<p>对于前、中两种遍历，实际以上代码从右子树向回走时，并未走完全程（stack 提前出栈了）后序遍历以上代码是走完全程了</p>
<p><strong>统一写法</strong></p>
<p>下面是一种统一的写法，依据后序遍历修改</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line">LinkedList&lt;TreeNode&gt; stack = <span class="keyword">new</span> <span class="title class_">LinkedList</span>&lt;&gt;();</span><br><span class="line"></span><br><span class="line"><span class="type">TreeNode</span> <span class="variable">curr</span> <span class="operator">=</span> root; <span class="comment">// 代表当前节点</span></span><br><span class="line"><span class="type">TreeNode</span> <span class="variable">pop</span> <span class="operator">=</span> <span class="literal">null</span>; <span class="comment">// 最近一次弹栈的元素</span></span><br><span class="line"><span class="keyword">while</span> (curr != <span class="literal">null</span> || !stack.isEmpty()) &#123;</span><br><span class="line">    <span class="keyword">if</span> (curr != <span class="literal">null</span>) &#123;</span><br><span class="line">        colorPrintln(<span class="string">&quot;前: &quot;</span> + curr.val, <span class="number">31</span>);</span><br><span class="line">        stack.push(curr); <span class="comment">// 压入栈，为了记住回来的路</span></span><br><span class="line">        curr = curr.left;</span><br><span class="line">    &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">        <span class="type">TreeNode</span> <span class="variable">peek</span> <span class="operator">=</span> stack.peek();</span><br><span class="line">        <span class="comment">// 右子树可以不处理, 对中序来说, 要在右子树处理之前打印</span></span><br><span class="line">        <span class="keyword">if</span> (peek.right == <span class="literal">null</span>) &#123;</span><br><span class="line">            colorPrintln(<span class="string">&quot;中: &quot;</span> + peek.val, <span class="number">36</span>);</span><br><span class="line">            pop = stack.pop();</span><br><span class="line">            colorPrintln(<span class="string">&quot;后: &quot;</span> + pop.val, <span class="number">34</span>);</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="comment">// 右子树处理完成, 对中序来说, 无需打印</span></span><br><span class="line">        <span class="keyword">else</span> <span class="keyword">if</span> (peek.right == pop) &#123;</span><br><span class="line">            pop = stack.pop();</span><br><span class="line">            colorPrintln(<span class="string">&quot;后: &quot;</span> + pop.val, <span class="number">34</span>);</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="comment">// 右子树待处理, 对中序来说, 要在右子树处理之前打印</span></span><br><span class="line">        <span class="keyword">else</span> &#123;</span><br><span class="line">            colorPrintln(<span class="string">&quot;中: &quot;</span> + peek.val, <span class="number">36</span>);</span><br><span class="line">            curr = peek.right;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">colorPrintln</span><span class="params">(String origin, <span class="type">int</span> color)</span> &#123;</span><br><span class="line">    System.out.printf(<span class="string">&quot;\033[%dm%s\033[0m%n&quot;</span>, color, origin);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>一张图演示三种遍历</p>
<p><img src= "" data-lazy-src="http://rx6zk4j2b.hn-bkt.clouddn.com/blogs/Sorted_binary_tree_ALL_RGB.svg.png" alt="Sorted_binary_tree_ALL_RGB.svg"></p>
<ul>
<li>红色：前序遍历顺序</li>
<li>绿色：中序遍历顺序</li>
<li>蓝色：后续遍历顺序</li>
</ul>
<h1 id="三-练习"><a href="#三-练习" class="headerlink" title="三. 练习"></a>三. 练习</h1><h2 id="3-1-时间复杂度"><a href="#3-1-时间复杂度" class="headerlink" title="3.1 时间复杂度"></a>3.1 时间复杂度</h2><p>用函数 $f(n)$ 表示算法效率与数据规模的关系，假设每次解决问题需要 1 微秒（$10^{-6}$ 秒），进行估算：</p>
<ol>
<li>如果 $f(n) &#x3D; n^2$ 那么 1 秒能解决多少次问题？1 天呢？</li>
<li>如果 $f(n) &#x3D; log_2(n)$  那么 1 秒能解决多少次问题？1 天呢？</li>
<li>如果 $f(n) &#x3D; n!$ 那么 1 秒能解决多少次问题？1 天呢？</li>
</ol>
<p>参考解答</p>
<ol>
<li>1秒 $\sqrt{10^6} &#x3D; 1000$ 次，1 天 $\sqrt{10^6 * 3600 * 24} \approx 293938$ 次</li>
<li>1秒 $2^{1,000,000} $ 次，一天 $2^{86,400,000,000}$</li>
<li>推算如下<ul>
<li>$10! &#x3D; 3,628,800$ 1秒能解决 $1,000,000$ 次，因此次数为 9 次</li>
<li>$14!&#x3D;87,178,291,200$，一天能解决 $86,400,000,000$ 次，因此次数为 13 次</li>
</ul>
</li>
</ol>
<h2 id="3-2-二分查找"><a href="#3-2-二分查找" class="headerlink" title="3.2 二分查找"></a>3.2 二分查找</h2><ul>
<li><a target="_blank" rel="noopener" href="https://leetcode.cn/problems/sqrtx/">69. x 的平方根 - 力扣（LeetCode）</a></li>
</ul>
<h3 id="E01-二分查找-力扣-704-题"><a href="#E01-二分查找-力扣-704-题" class="headerlink" title="E01. 二分查找-力扣 704 题"></a>E01. 二分查找-力扣 704 题</h3><p><strong>要点</strong>：减而治之，可以用递归或非递归实现</p>
<p>给定一个 n 个元素有序的（升序）整型数组 nums 和一个目标值 target  ，写一个函数搜索 nums 中的 target，如果目标值存在返回下标，否则返回 -1</p>
<p>例如</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line">输入: nums = [-<span class="number">1</span>,<span class="number">0</span>,<span class="number">3</span>,<span class="number">5</span>,<span class="number">9</span>,<span class="number">12</span>], target = <span class="number">9</span></span><br><span class="line">输出: <span class="number">4</span></span><br><span class="line">解释: <span class="number">9</span> 出现在 nums 中并且下标为 <span class="number">4</span></span><br><span class="line">    </span><br><span class="line">输入: nums = [-<span class="number">1</span>,<span class="number">0</span>,<span class="number">3</span>,<span class="number">5</span>,<span class="number">9</span>,<span class="number">12</span>], target = <span class="number">2</span></span><br><span class="line">输出: -<span class="number">1</span></span><br><span class="line">解释: <span class="number">2</span> 不存在 nums 中因此返回 -<span class="number">1</span>    </span><br></pre></td></tr></table></figure>

<p><strong>参考答案</strong>：略，可以用讲过的任意一种二分求解</p>
<h3 id="E02-搜索插入位置-力扣-35-题"><a href="#E02-搜索插入位置-力扣-35-题" class="headerlink" title="E02. 搜索插入位置-力扣 35 题"></a>E02. 搜索插入位置-力扣 35 题</h3><p><strong>要点</strong>：理解谁代表插入位置</p>
<p>给定一个排序数组和一个目标值</p>
<ul>
<li>在数组中找到目标值，并返回其索引</li>
<li>如果目标值不存在于数组中，返回它将会被按顺序插入的位置</li>
</ul>
<p>例如</p>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">输入: nums = [1,3,5,6], target = 5</span><br><span class="line">输出: 2</span><br><span class="line"></span><br><span class="line">输入: nums = [1,3,5,6], target = 2</span><br><span class="line">输出: 1</span><br><span class="line"></span><br><span class="line">输入: nums = [1,3,5,6], target = 7</span><br><span class="line">输出: 4</span><br></pre></td></tr></table></figure>



<p><strong>参考答案1</strong>：用二分查找基础版代码改写，基础版中，找到返回 m，没找到 i 代表插入点，因此有</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="type">int</span> <span class="title function_">searchInsert</span><span class="params">(<span class="type">int</span>[] a, <span class="type">int</span> target)</span> &#123;</span><br><span class="line">    <span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> <span class="number">0</span>, j = a.length - <span class="number">1</span>;</span><br><span class="line">    <span class="keyword">while</span> (i &lt;= j) &#123;</span><br><span class="line">        <span class="type">int</span> <span class="variable">m</span> <span class="operator">=</span> (i + j) &gt;&gt;&gt; <span class="number">1</span>;</span><br><span class="line">        <span class="keyword">if</span> (target &lt; a[m]) &#123;</span><br><span class="line">            j = m - <span class="number">1</span>;</span><br><span class="line">        &#125; <span class="keyword">else</span> <span class="keyword">if</span> (a[m] &lt; target) &#123;</span><br><span class="line">            i = m + <span class="number">1</span>;</span><br><span class="line">        &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">            <span class="keyword">return</span> m;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> i; <span class="comment">// 原始 return -1</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>



<p><strong>参考答案2</strong>：用二分查找平衡版改写，平衡版中</p>
<ul>
<li>如果 target &#x3D;&#x3D; a[i] 返回 i 表示找到</li>
<li>如果 target &lt; a[i]，例如 target &#x3D; 2，a[i] &#x3D; 3，这时就应该在 i 位置插入 2</li>
<li>如果 a[i] &lt; target，例如 a[i] &#x3D; 3，target &#x3D; 4，这时就应该在 i+1 位置插入 4</li>
</ul>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">static</span> <span class="type">int</span> <span class="title function_">searchInsert</span><span class="params">(<span class="type">int</span>[] a, <span class="type">int</span> target)</span> &#123;</span><br><span class="line">    <span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> <span class="number">0</span>, j = a.length;</span><br><span class="line">    <span class="keyword">while</span> (<span class="number">1</span> &lt; j - i) &#123;</span><br><span class="line">        <span class="type">int</span> <span class="variable">m</span> <span class="operator">=</span> (i + j) &gt;&gt;&gt; <span class="number">1</span>;</span><br><span class="line">        <span class="keyword">if</span> (target &lt; a[m]) &#123;</span><br><span class="line">            j = m;</span><br><span class="line">        &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">            i = m;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> (target &lt;= a[i]) ? i : i + <span class="number">1</span>;</span><br><span class="line">    <span class="comment">// 原始 (target == a[i]) ? i : -1;</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>



<p><strong>参考答案3</strong>：用 leftmost 版本解，返回值即为插入位置（并能处理元素重复的情况）</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="type">int</span> <span class="title function_">searchInsert</span><span class="params">(<span class="type">int</span>[] a, <span class="type">int</span> target)</span> &#123;</span><br><span class="line">    <span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> <span class="number">0</span>, j = a.length - <span class="number">1</span>;</span><br><span class="line">    <span class="keyword">while</span>(i &lt;= j) &#123;</span><br><span class="line">        <span class="type">int</span> <span class="variable">m</span> <span class="operator">=</span> (i + j) &gt;&gt;&gt; <span class="number">1</span>;</span><br><span class="line">        <span class="keyword">if</span>(target &lt;= a[m]) &#123;</span><br><span class="line">            j = m - <span class="number">1</span>;</span><br><span class="line">        &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">            i = m + <span class="number">1</span>;</span><br><span class="line">        &#125; </span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> i;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>



<h3 id="E03-搜索开始结束位置-力扣-34-题"><a href="#E03-搜索开始结束位置-力扣-34-题" class="headerlink" title="E03. 搜索开始结束位置-力扣 34 题"></a>E03. 搜索开始结束位置-力扣 34 题</h3><p>给你一个按照非递减顺序排列的整数数组 nums，和一个目标值 target。请你找出给定目标值在数组中的开始位置和结束位置。</p>
<p>如果数组中不存在目标值 target，返回 [-1, -1]。</p>
<p>你必须设计并实现时间复杂度为 O(log n) 的算法解决此问题</p>
<p>例如</p>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">输入：nums = [5,7,7,8,8,10], target = 8</span><br><span class="line">输出：[3,4]</span><br><span class="line"></span><br><span class="line">输入：nums = [5,7,7,8,8,10], target = 6</span><br><span class="line">输出：[-1,-1]</span><br><span class="line"></span><br><span class="line">输入：nums = [], target = 0</span><br><span class="line">输出：[-1,-1]</span><br></pre></td></tr></table></figure>



<p><strong>参考答案</strong></p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">static</span> <span class="type">int</span> <span class="title function_">left</span><span class="params">(<span class="type">int</span>[] a, <span class="type">int</span> target)</span> &#123;</span><br><span class="line">    <span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> <span class="number">0</span>, j = a.length - <span class="number">1</span>;</span><br><span class="line">    <span class="type">int</span> <span class="variable">candidate</span> <span class="operator">=</span> -<span class="number">1</span>;</span><br><span class="line">    <span class="keyword">while</span> (i &lt;= j) &#123;</span><br><span class="line">        <span class="type">int</span> <span class="variable">m</span> <span class="operator">=</span> (i + j) &gt;&gt;&gt; <span class="number">1</span>;</span><br><span class="line">        <span class="keyword">if</span> (target &lt; a[m]) &#123;</span><br><span class="line">            j = m - <span class="number">1</span>;</span><br><span class="line">        &#125; <span class="keyword">else</span> <span class="keyword">if</span> (a[m] &lt; target) &#123;</span><br><span class="line">            i = m + <span class="number">1</span>;</span><br><span class="line">        &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">            candidate = m;</span><br><span class="line">            j = m - <span class="number">1</span>;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> candidate;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">static</span> <span class="type">int</span> <span class="title function_">right</span><span class="params">(<span class="type">int</span>[] a, <span class="type">int</span> target)</span> &#123;</span><br><span class="line">    <span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> <span class="number">0</span>, j = a.length - <span class="number">1</span>;</span><br><span class="line">    <span class="type">int</span> <span class="variable">candidate</span> <span class="operator">=</span> -<span class="number">1</span>;</span><br><span class="line">    <span class="keyword">while</span> (i &lt;= j) &#123;</span><br><span class="line">        <span class="type">int</span> <span class="variable">m</span> <span class="operator">=</span> (i + j) &gt;&gt;&gt; <span class="number">1</span>;</span><br><span class="line">        <span class="keyword">if</span> (target &lt; a[m]) &#123;</span><br><span class="line">            j = m - <span class="number">1</span>;</span><br><span class="line">        &#125; <span class="keyword">else</span> <span class="keyword">if</span> (a[m] &lt; target) &#123;</span><br><span class="line">            i = m + <span class="number">1</span>;</span><br><span class="line">        &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">            candidate = m;</span><br><span class="line">            i = m + <span class="number">1</span>;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> candidate;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">static</span> <span class="type">int</span>[] searchRange(<span class="type">int</span>[] nums, <span class="type">int</span> target) &#123;</span><br><span class="line">    <span class="type">int</span> <span class="variable">x</span> <span class="operator">=</span> left(nums, target);</span><br><span class="line">    <span class="keyword">if</span>(x == -<span class="number">1</span>) &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">new</span> <span class="title class_">int</span>[] &#123;-<span class="number">1</span>, -<span class="number">1</span>&#125;;</span><br><span class="line">    &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">new</span> <span class="title class_">int</span>[] &#123;x, right(nums, target)&#125;;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>



<h2 id="3-3-递归-single-recursion"><a href="#3-3-递归-single-recursion" class="headerlink" title="3.3 递归 - single recursion"></a>3.3 递归 - single recursion</h2><h3 id="E03-二分查找"><a href="#E03-二分查找" class="headerlink" title="E03. 二分查找"></a>E03. 二分查找</h3><figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">static</span> <span class="type">int</span> <span class="title function_">binarySearch</span><span class="params">(<span class="type">int</span>[] a, <span class="type">int</span> target)</span> &#123;</span><br><span class="line">    <span class="keyword">return</span> recursion(a, target, <span class="number">0</span>, a.length - <span class="number">1</span>);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">static</span> <span class="type">int</span> <span class="title function_">recursion</span><span class="params">(<span class="type">int</span>[] a, <span class="type">int</span> target, <span class="type">int</span> i, <span class="type">int</span> j)</span> &#123;</span><br><span class="line">    <span class="keyword">if</span> (i &gt; j) &#123;</span><br><span class="line">        <span class="keyword">return</span> -<span class="number">1</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="type">int</span> <span class="variable">m</span> <span class="operator">=</span> (i + j) &gt;&gt;&gt; <span class="number">1</span>;</span><br><span class="line">    <span class="keyword">if</span> (target &lt; a[m]) &#123;</span><br><span class="line">        <span class="keyword">return</span> recursion(a, target, i, m - <span class="number">1</span>);</span><br><span class="line">    &#125; <span class="keyword">else</span> <span class="keyword">if</span> (a[m] &lt; target) &#123;</span><br><span class="line">        <span class="keyword">return</span> recursion(a, target, m + <span class="number">1</span>, j);</span><br><span class="line">    &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> m;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>



<h3 id="E04-冒泡排序"><a href="#E04-冒泡排序" class="headerlink" title="E04. 冒泡排序"></a>E04. 冒泡排序</h3><figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> &#123;</span><br><span class="line">    <span class="type">int</span>[] a = &#123;<span class="number">3</span>, <span class="number">2</span>, <span class="number">6</span>, <span class="number">1</span>, <span class="number">5</span>, <span class="number">4</span>, <span class="number">7</span>&#125;;</span><br><span class="line">    bubble(a, <span class="number">0</span>, a.length - <span class="number">1</span>);</span><br><span class="line">    System.out.println(Arrays.toString(a));</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">bubble</span><span class="params">(<span class="type">int</span>[] a, <span class="type">int</span> low, <span class="type">int</span> high)</span> &#123;</span><br><span class="line">    <span class="keyword">if</span>(low == high) &#123;</span><br><span class="line">        <span class="keyword">return</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="type">int</span> <span class="variable">j</span> <span class="operator">=</span> low;</span><br><span class="line">    <span class="keyword">for</span> (<span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> low; i &lt; high; i++) &#123;</span><br><span class="line">        <span class="keyword">if</span> (a[i] &gt; a[i + <span class="number">1</span>]) &#123;</span><br><span class="line">            swap(a, i, i + <span class="number">1</span>);</span><br><span class="line">            j = i;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    bubble(a, low, j);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">swap</span><span class="params">(<span class="type">int</span>[] a, <span class="type">int</span> i, <span class="type">int</span> j)</span> &#123;</span><br><span class="line">    <span class="type">int</span> <span class="variable">t</span> <span class="operator">=</span> a[i];</span><br><span class="line">    a[i] = a[j];</span><br><span class="line">    a[j] = t;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<ul>
<li>low 与 high 为未排序范围</li>
<li>j 表示的是未排序的边界，下一次递归时的 high<ul>
<li>发生交换，意味着有无序情况</li>
<li>最后一次交换（以后没有无序）时，左侧 i 仍是无序，右侧 i+1 已然有序</li>
</ul>
</li>
<li>视频中讲解的是只考虑 high 边界的情况，参考以上代码，理解在 low .. high 范围内的处理方法</li>
</ul>
<h3 id="E05-插入排序"><a href="#E05-插入排序" class="headerlink" title="E05. 插入排序"></a>E05. 插入排序</h3><figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> &#123;</span><br><span class="line">    <span class="type">int</span>[] a = &#123;<span class="number">3</span>, <span class="number">2</span>, <span class="number">6</span>, <span class="number">1</span>, <span class="number">5</span>, <span class="number">7</span>, <span class="number">4</span>&#125;;</span><br><span class="line">    insertion(a, <span class="number">1</span>, a.length - <span class="number">1</span>);</span><br><span class="line">    System.out.println(Arrays.toString(a));</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">insertion</span><span class="params">(<span class="type">int</span>[] a, <span class="type">int</span> low, <span class="type">int</span> high)</span> &#123;</span><br><span class="line">    <span class="keyword">if</span> (low &gt; high) &#123;</span><br><span class="line">        <span class="keyword">return</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> low - <span class="number">1</span>;</span><br><span class="line">    <span class="type">int</span> <span class="variable">t</span> <span class="operator">=</span> a[low];</span><br><span class="line">    <span class="keyword">while</span> (i &gt;= <span class="number">0</span> &amp;&amp; a[i] &gt; i) &#123;</span><br><span class="line">        a[i + <span class="number">1</span>] = a[i];</span><br><span class="line">        i--;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">if</span>(i + <span class="number">1</span> != low) &#123;</span><br><span class="line">        a[i + <span class="number">1</span>] = t;</span><br><span class="line">    &#125;    </span><br><span class="line">    insertion(a, low + <span class="number">1</span>, high);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<ul>
<li>已排序区域：[0 .. i .. low-1]</li>
<li>未排序区域：[low .. high]</li>
<li>视频中讲解的是只考虑 low 边界的情况，参考以上代码，理解 low-1 ..  high 范围内的处理方法</li>
<li>扩展：利用二分查找 leftmost 版本，改进寻找插入位置的代码</li>
</ul>
<h3 id="E06-约瑟夫问题-16"><a href="#E06-约瑟夫问题-16" class="headerlink" title="E06. 约瑟夫问题[^16]"></a>E06. 约瑟夫问题[^16]</h3><p>$n$ 个人排成圆圈，从头开始报数，每次数到第 $m$ 个人（$m$ 从 $1$ 开始）杀之，继续从下一个人重复以上过程，求最后活下来的人是谁？</p>
<p><strong>方法1</strong></p>
<p>根据最后的存活者 a 倒推出它在上一轮的索引号</p>
<table>
<thead>
<tr>
<th>f(n,m)</th>
<th>本轮索引</th>
<th>为了让 a 是这个索引，上一轮应当这样排</th>
<th>规律</th>
</tr>
</thead>
<tbody><tr>
<td>f(1,3)</td>
<td>0</td>
<td>x x x a</td>
<td>(0 + 3) % 2</td>
</tr>
<tr>
<td>f(2,3)</td>
<td>1</td>
<td>x x x 0 a</td>
<td>(1 + 3) % 3</td>
</tr>
<tr>
<td>f(3,3)</td>
<td>1</td>
<td>x x x 0 a</td>
<td>(1 + 3) % 4</td>
</tr>
<tr>
<td>f(4,3)</td>
<td>0</td>
<td>x x x a</td>
<td>(0 + 3) % 5</td>
</tr>
<tr>
<td>f(5,3)</td>
<td>3</td>
<td>x x x 0 1 2 a</td>
<td>(3 + 3) % 6</td>
</tr>
<tr>
<td>f(6,3)</td>
<td>0</td>
<td>x x x a</td>
<td></td>
</tr>
</tbody></table>
<p><strong>方法2</strong></p>
<p>设 n 为总人数，m 为报数次数，解返回的是这些人的索引，从0开始</p>
<table>
<thead>
<tr>
<th>f(n, m)</th>
<th>解</th>
<th>规律</th>
</tr>
</thead>
<tbody><tr>
<td>f(1, 3)</td>
<td>0</td>
<td></td>
</tr>
<tr>
<td>f(2, 3)</td>
<td>0 1 &#x3D;&gt; 1</td>
<td>3%2&#x3D;1</td>
</tr>
<tr>
<td>f(3, 3)</td>
<td>0 1 2 &#x3D;&gt; 0 1</td>
<td>3%3&#x3D;0</td>
</tr>
<tr>
<td>f(4, 3)</td>
<td>0 1 2 3 &#x3D;&gt; 3 0 1</td>
<td>3%4&#x3D;3</td>
</tr>
<tr>
<td>f(5, 3)</td>
<td>0 1 2 3 4  &#x3D;&gt; 3 4 0 1</td>
<td>3%5&#x3D;3</td>
</tr>
<tr>
<td>f(6, 3)</td>
<td>0 1 2 3 4 5 &#x3D;&gt; 3 4 5 0 1</td>
<td>3%6&#x3D;3</td>
</tr>
</tbody></table>
<p><strong>一. 找出等价函数</strong></p>
<p>规律：下次报数的起点为 $k &#x3D; m % n$</p>
<ul>
<li>首次出列人的序号是 $k-1$，剩下的的 $n-1$ 个人重新组成约瑟夫环</li>
<li>下次从 $k$ 开始数，序号如下<ul>
<li>$k,\ k+1, \ …\ ,\ 0,\ 1,\ k-2$，如上例中 $3\ 4\ 5\ 0\ 1$</li>
</ul>
</li>
</ul>
<p>这个函数称之为 $g(n-1,m)$，它的最终结果与 $f(n,m)$ 是相同的。</p>
<p><strong>二. 找到映射函数</strong></p>
<p>现在想办法找到 $g(n-1,m)$ 与 $f(n-1, m)$ 的对应关系，即<br>$$<br>3 \rightarrow 0 \<br>4 \rightarrow 1 \<br>5 \rightarrow 2 \<br>0 \rightarrow 3 \<br>1 \rightarrow 4 \<br>$$<br>映射函数为<br>$$<br>mapping(x) &#x3D;<br>\begin{cases}<br>x-k &amp; x&#x3D;[k..n-1] \<br>x+n-k &amp; x&#x3D;[0..k-2]<br>\end{cases}<br>$$<br>等价于下面函数<br>$$<br>mapping(x) &#x3D; (x + n - k)%{n}<br>$$<br>代入测试一下<br>$$<br>3 \rightarrow (3+6-3)%6 \rightarrow 0 \<br>4 \rightarrow (4+6-3)%6 \rightarrow 1 \<br>5 \rightarrow (5+6-3)%6 \rightarrow 2 \<br>0 \rightarrow (0+6-3)%6 \rightarrow 3 \<br>1 \rightarrow (1+6-3)%6 \rightarrow 4 \<br>$$<br>综上有<br>$$<br>f(n-1,m) &#x3D; mapping(g(n-1,m))<br>$$</p>
<p><strong>三. 求逆映射函数</strong></p>
<p>映射函数是根据 x 计算 y，逆映射函数即根据 y 得到 x<br>$$<br>mapping^{-1}(x) &#x3D; (x + k)%n<br>$$<br>代入测试一下<br>$$<br>0 \rightarrow (0+3)%6 \rightarrow 3 \<br>1 \rightarrow (1+3)%6 \rightarrow 4 \<br>2 \rightarrow (2+3)%6 \rightarrow 5 \<br>3 \rightarrow (3+3)%6 \rightarrow 0 \<br>4 \rightarrow (4+3)%6 \rightarrow 1 \<br>$$<br>因此可以求得<br>$$<br>g(n-1,m) &#x3D; mapping^{-1}(f(n-1,m))<br>$$</p>
<p><strong>四. 递推式</strong></p>
<p>代入推导<br>$$<br>\begin{aligned}<br>f(n,m) &#x3D; \ &amp; g(n-1,m) \<br>&#x3D; \ &amp; mapping^{-1}(f(n-1,m)) \<br>&#x3D; \ &amp; (f(n-1,m) + k) % n \<br>&#x3D; \ &amp; (f(n-1,m) + m%n) % n \<br>&#x3D; \ &amp; (f(n-1,m) + m) % n \<br>\end{aligned}<br>$$<br>最后一步化简是利用了模运算法则</p>
<p>$(a+b)%n &#x3D; (a%n + b%n) %n$  例如 </p>
<ul>
<li>$(6+6)%5 &#x3D; 2 &#x3D; (6+6%5)%5$</li>
<li>$(6+5)%5 &#x3D; 1 &#x3D; (6+5%5)%5$</li>
<li>$(6+4)%5 &#x3D; 0 &#x3D; (6+4%5)%5$</li>
</ul>
<p>最终递推式<br>$$<br>f(n,m) &#x3D;<br>\begin{cases}<br>(f(n-1,m) + m) % n &amp; n&gt;1\<br>0 &amp; n &#x3D; 1<br>\end{cases}<br>$$</p>
<h2 id="3-4-递归-multi-recursion"><a href="#3-4-递归-multi-recursion" class="headerlink" title="3.4 递归 - multi recursion"></a>3.4 递归 - multi recursion</h2><h3 id="E02-汉诺塔-13"><a href="#E02-汉诺塔-13" class="headerlink" title="E02. 汉诺塔[^13]"></a>E02. 汉诺塔[^13]</h3><p>Tower of Hanoi，是一个源于印度古老传说：大梵天创建世界时做了三根金刚石柱，在一根柱子从下往上按大小顺序摞着 64 片黄金圆盘，大梵天命令婆罗门把圆盘重新摆放在另一根柱子上，并且规定</p>
<ul>
<li>一次只能移动一个圆盘</li>
<li>小圆盘上不能放大圆盘</li>
</ul>
<p>下面的动图演示了4片圆盘的移动方法</p>
<p><img src= "" data-lazy-src="http://rx6zk4j2b.hn-bkt.clouddn.com/blogs/Tower_of_Hanoi_4.gif"></p>
<p>使用程序代码模拟圆盘的移动过程，并估算出时间复杂度</p>
<p><strong>思路</strong></p>
<ul>
<li><p>假设每根柱子标号 a，b，c，每个圆盘用 1，2，3 … 表示其大小，圆盘初始在 a，要移动到的目标是 c</p>
</li>
<li><p>如果只有一个圆盘，此时是最小问题，可以直接求解</p>
<ul>
<li>移动圆盘1 $a \mapsto c$</li>
</ul>
<img src= "" data-lazy-src="http://rx6zk4j2b.hn-bkt.clouddn.com/blogs/image-20221219090741078.png" alt="image-20221219090741078" style="zoom:50%;" />
</li>
<li><p>如果有两个圆盘，那么</p>
<ul>
<li>圆盘1 $a \mapsto b$ </li>
<li>圆盘2 $a \mapsto c$</li>
<li>圆盘1 $b \mapsto c$</li>
</ul>
<img src= "" data-lazy-src="http://rx6zk4j2b.hn-bkt.clouddn.com/blogs/image-20221219091316225.png" alt="image-20221219091316225" style="zoom:50%;" />
</li>
<li><p>如果有三个圆盘，那么</p>
<ul>
<li>圆盘12 $a \mapsto b$</li>
<li>圆盘3 $a \mapsto c$</li>
<li>圆盘12 $b \mapsto c$</li>
</ul>
<img src= "" data-lazy-src="http://rx6zk4j2b.hn-bkt.clouddn.com/blogs/image-20221219091930614.png" alt="image-20221219091930614" style="zoom:50%;" />
</li>
<li><p>如果有四个圆盘，那么</p>
<ul>
<li>圆盘 123 $a \mapsto b$</li>
<li>圆盘4 $a \mapsto c$</li>
<li>圆盘 123 $b \mapsto c$</li>
</ul>
</li>
</ul>
<img src= "" data-lazy-src="http://rx6zk4j2b.hn-bkt.clouddn.com/blogs/image-20221219092537323.png" alt="image-20221219092537323" style="zoom:50%;" />



<p><strong>题解</strong></p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">E02HanoiTower</span> &#123;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    <span class="comment">/*</span></span><br><span class="line"><span class="comment">             源 借 目</span></span><br><span class="line"><span class="comment">        h(4, a, b, c) -&gt; h(3, a, c, b)</span></span><br><span class="line"><span class="comment">                         a -&gt; c</span></span><br><span class="line"><span class="comment">                         h(3, b, a, c)</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">static</span> LinkedList&lt;Integer&gt; a = <span class="keyword">new</span> <span class="title class_">LinkedList</span>&lt;&gt;();</span><br><span class="line">    <span class="keyword">static</span> LinkedList&lt;Integer&gt; b = <span class="keyword">new</span> <span class="title class_">LinkedList</span>&lt;&gt;();</span><br><span class="line">    <span class="keyword">static</span> LinkedList&lt;Integer&gt; c = <span class="keyword">new</span> <span class="title class_">LinkedList</span>&lt;&gt;();</span><br><span class="line"></span><br><span class="line">    <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">init</span><span class="params">(<span class="type">int</span> n)</span> &#123;</span><br><span class="line">        <span class="keyword">for</span> (<span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> n; i &gt;= <span class="number">1</span>; i--) &#123;</span><br><span class="line">            a.add(i);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">h</span><span class="params">(<span class="type">int</span> n, LinkedList&lt;Integer&gt; a, </span></span><br><span class="line"><span class="params">                  LinkedList&lt;Integer&gt; b, </span></span><br><span class="line"><span class="params">                  LinkedList&lt;Integer&gt; c)</span> &#123;</span><br><span class="line">        <span class="keyword">if</span> (n == <span class="number">0</span>) &#123;</span><br><span class="line">            <span class="keyword">return</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        h(n - <span class="number">1</span>, a, c, b);</span><br><span class="line">        c.addLast(a.removeLast());</span><br><span class="line">        print();</span><br><span class="line">        h(n - <span class="number">1</span>, b, a, c);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">print</span><span class="params">()</span> &#123;</span><br><span class="line">        System.out.println(<span class="string">&quot;-----------------------&quot;</span>);</span><br><span class="line">        System.out.println(a);</span><br><span class="line">        System.out.println(b);</span><br><span class="line">        System.out.println(c);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> &#123;</span><br><span class="line">        init(<span class="number">3</span>);</span><br><span class="line">        print();</span><br><span class="line">        h(<span class="number">3</span>, a, b, c);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>



<h3 id="E03-杨辉三角-6"><a href="#E03-杨辉三角-6" class="headerlink" title="E03. 杨辉三角[^6]"></a>E03. 杨辉三角[^6]</h3><p><img src= "" data-lazy-src="http://rx6zk4j2b.hn-bkt.clouddn.com/blogs/image-20221219172514410.png" alt="image-20221219172514410"></p>
<p><strong>分析</strong></p>
<p>把它斜着看</p>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">        1</span><br><span class="line">      1   1</span><br><span class="line">    1   2   1</span><br><span class="line">  1   3   3   1</span><br><span class="line">1   4   6   4   1</span><br></pre></td></tr></table></figure>

<ul>
<li>行 $i$，列 $j$，那么 $[i][j]$ 的取值应为 $[i-1][j-1] + [i-1][j]$</li>
<li>当 $j&#x3D;0$ 或 $i&#x3D;j$ 时，$[i][j]$ 取值为 $1$</li>
</ul>
<p><strong>题解</strong></p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">print</span><span class="params">(<span class="type">int</span> n)</span> &#123;</span><br><span class="line">    <span class="keyword">for</span> (<span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> <span class="number">0</span>; i &lt; n; i++) &#123;</span><br><span class="line">        <span class="keyword">if</span> (i &lt; n - <span class="number">1</span>) &#123;</span><br><span class="line">            System.out.printf(<span class="string">&quot;%&quot;</span> + <span class="number">2</span> * (n - <span class="number">1</span> - i) + <span class="string">&quot;s&quot;</span>, <span class="string">&quot; &quot;</span>);</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">for</span> (<span class="type">int</span> <span class="variable">j</span> <span class="operator">=</span> <span class="number">0</span>; j &lt; i + <span class="number">1</span>; j++) &#123;</span><br><span class="line">            System.out.printf(<span class="string">&quot;%-4d&quot;</span>, element(i, j));</span><br><span class="line">        &#125;</span><br><span class="line">        System.out.println();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">static</span> <span class="type">int</span> <span class="title function_">element</span><span class="params">(<span class="type">int</span> i, <span class="type">int</span> j)</span> &#123;</span><br><span class="line">    <span class="keyword">if</span> (j == <span class="number">0</span> || i == j) &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="number">1</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> element(i - <span class="number">1</span>, j - <span class="number">1</span>) + element(i - <span class="number">1</span>, j);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>



<p><strong>优化1</strong></p>
<p>是 multiple recursion，因此很多递归调用是重复的，例如</p>
<ul>
<li>recursion(3, 1) 分解为<ul>
<li>recursion(2, 0) + recursion(2, 1)</li>
</ul>
</li>
<li>而 recursion(3, 2) 分解为<ul>
<li>recursion(2, 1) + recursion(2, 2)</li>
</ul>
</li>
</ul>
<p>这里 recursion(2, 1) 就重复调用了，事实上它会重复很多次，可以用 static AtomicInteger counter &#x3D; new AtomicInteger(0) 来查看递归函数的调用总次数</p>
<p>事实上，可以用 <strong>memoization</strong> 来进行优化：</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">print1</span><span class="params">(<span class="type">int</span> n)</span> &#123;</span><br><span class="line">    <span class="type">int</span>[][] triangle = <span class="keyword">new</span> <span class="title class_">int</span>[n][];</span><br><span class="line">    <span class="keyword">for</span> (<span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> <span class="number">0</span>; i &lt; n; i++) &#123;</span><br><span class="line">        <span class="comment">// 打印空格</span></span><br><span class="line">        triangle[i] = <span class="keyword">new</span> <span class="title class_">int</span>[i + <span class="number">1</span>];</span><br><span class="line">        <span class="keyword">for</span> (<span class="type">int</span> <span class="variable">j</span> <span class="operator">=</span> <span class="number">0</span>; j &lt;= i; j++) &#123;</span><br><span class="line">            System.out.printf(<span class="string">&quot;%-4d&quot;</span>, element1(triangle, i, j));</span><br><span class="line">        &#125;</span><br><span class="line">        System.out.println();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">static</span> <span class="type">int</span> <span class="title function_">element1</span><span class="params">(<span class="type">int</span>[][] triangle, <span class="type">int</span> i, <span class="type">int</span> j)</span> &#123;</span><br><span class="line">    <span class="keyword">if</span> (triangle[i][j] &gt; <span class="number">0</span>) &#123;</span><br><span class="line">        <span class="keyword">return</span> triangle[i][j];</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span> (j == <span class="number">0</span> || i == j) &#123;</span><br><span class="line">        triangle[i][j] = <span class="number">1</span>;</span><br><span class="line">        <span class="keyword">return</span> triangle[i][j];</span><br><span class="line">    &#125;</span><br><span class="line">    triangle[i][j] = element1(triangle, i - <span class="number">1</span>, j - <span class="number">1</span>) + element1(triangle, i - <span class="number">1</span>, j);</span><br><span class="line">    <span class="keyword">return</span> triangle[i][j];</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<ul>
<li>将数组作为递归函数内可以访问的遍历，如果 $triangle[i][j]$ 已经有值，说明该元素已经被之前的递归函数计算过，就不必重复计算了</li>
</ul>
<p><strong>优化2</strong></p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">print2</span><span class="params">(<span class="type">int</span> n)</span> &#123;</span><br><span class="line">    <span class="type">int</span>[] row = <span class="keyword">new</span> <span class="title class_">int</span>[n];</span><br><span class="line">    <span class="keyword">for</span> (<span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> <span class="number">0</span>; i &lt; n; i++) &#123;</span><br><span class="line">        <span class="comment">// 打印空格</span></span><br><span class="line">        createRow(row, i);</span><br><span class="line">        <span class="keyword">for</span> (<span class="type">int</span> <span class="variable">j</span> <span class="operator">=</span> <span class="number">0</span>; j &lt;= i; j++) &#123;</span><br><span class="line">            System.out.printf(<span class="string">&quot;%-4d&quot;</span>, row[j]);</span><br><span class="line">        &#125;</span><br><span class="line">        System.out.println();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">createRow</span><span class="params">(<span class="type">int</span>[] row, <span class="type">int</span> i)</span> &#123;</span><br><span class="line">    <span class="keyword">if</span> (i == <span class="number">0</span>) &#123;</span><br><span class="line">        row[<span class="number">0</span>] = <span class="number">1</span>;</span><br><span class="line">        <span class="keyword">return</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">for</span> (<span class="type">int</span> <span class="variable">j</span> <span class="operator">=</span> i; j &gt; <span class="number">0</span>; j--) &#123;</span><br><span class="line">        row[j] = row[j - <span class="number">1</span>] + row[j];</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<blockquote>
<p>注意：还可以通过每一行的前一项计算出下一项，不必借助上一行，这与杨辉三角的另一个特性有关，暂不展开了</p>
</blockquote>
<p>力扣对应题目，但递归不适合在力扣刷高分，因此只列出相关题目，不做刷题讲解了</p>
<ul>
<li><a target="_blank" rel="noopener" href="https://leetcode.cn/problems/pascals-triangle/">118. 杨辉三角 - 力扣（LeetCode）</a></li>
<li><a target="_blank" rel="noopener" href="https://leetcode.cn/problems/pascals-triangle-ii/solution/yang-hui-san-jiao-ii-by-leetcode-solutio-shuk/">119. 杨辉三角 II - 力扣（LeetCode）</a></li>
</ul>
<h2 id="3-5-链表"><a href="#3-5-链表" class="headerlink" title="3.5 链表"></a>3.5 链表</h2><h3 id="E01-反转单向链表-力扣-206-题"><a href="#E01-反转单向链表-力扣-206-题" class="headerlink" title="E01. 反转单向链表-力扣 206 题"></a>E01. 反转单向链表-力扣 206 题</h3><p>对应力扣题目 <a target="_blank" rel="noopener" href="https://leetcode.cn/problems/reverse-linked-list/">206. 反转链表 - 力扣（LeetCode）</a></p>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">输入：head = [1,2,3,4,5]</span><br><span class="line">输出：[5,4,3,2,1]</span><br><span class="line"></span><br><span class="line">输入：[1,2]</span><br><span class="line">输出：[2,1]</span><br><span class="line"></span><br><span class="line">输入：[]</span><br><span class="line">输出：[]</span><br></pre></td></tr></table></figure>



<p><strong>方法1</strong></p>
<p>构造一个新链表，从<strong>旧链表</strong>依次拿到每个节点，创建新节点添加至<strong>新链表</strong>头部，完成后新链表即是倒序的</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> ListNode <span class="title function_">reverseList</span><span class="params">(ListNode o1)</span> &#123;</span><br><span class="line">    <span class="type">ListNode</span> <span class="variable">n1</span> <span class="operator">=</span> <span class="literal">null</span>;</span><br><span class="line">    <span class="type">ListNode</span> <span class="variable">p</span> <span class="operator">=</span> o1;</span><br><span class="line">    <span class="keyword">while</span> (p != <span class="literal">null</span>) &#123;</span><br><span class="line">        n1 = <span class="keyword">new</span> <span class="title class_">ListNode</span>(p.val, n1);</span><br><span class="line">        p = p.next;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> n1;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>评价：简单直白，就是得新创建节点对象</p>
<p><strong>方法2</strong></p>
<p>与方法1 类似，构造一个新链表，从<strong>旧链表头部</strong>移除节点，添加到<strong>新链表头部</strong>，完成后新链表即是倒序的，区别在于原题目未提供节点外层的容器类，这里提供一个，另外一个区别是并不去构造新节点</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">static</span> <span class="keyword">class</span> <span class="title class_">List</span> &#123;</span><br><span class="line">    ListNode head;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="title function_">List</span><span class="params">(ListNode head)</span> &#123;</span><br><span class="line">        <span class="built_in">this</span>.head = head;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> ListNode <span class="title function_">removeFirst</span><span class="params">()</span>&#123;</span><br><span class="line">        <span class="type">ListNode</span> <span class="variable">first</span> <span class="operator">=</span> head;</span><br><span class="line">        <span class="keyword">if</span> (first != <span class="literal">null</span>) &#123;</span><br><span class="line">            head = first.next;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> first;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">addFirst</span><span class="params">(ListNode first)</span> &#123;</span><br><span class="line">        first.next = head;</span><br><span class="line">        head = first;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>代码</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> ListNode <span class="title function_">reverseList</span><span class="params">(ListNode head)</span> &#123;</span><br><span class="line">    <span class="type">List</span> <span class="variable">list1</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">List</span>(head);</span><br><span class="line">    <span class="type">List</span> <span class="variable">list2</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">List</span>(<span class="literal">null</span>);</span><br><span class="line">    ListNode first;</span><br><span class="line">    <span class="keyword">while</span> ((first = list1.removeFirst()) != <span class="literal">null</span>) &#123;</span><br><span class="line">        list2.addFirst(first);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> list2.head;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>评价：更加面向对象，如果实际写代码而非刷题，更多会这么做</p>
<p><strong>方法3</strong></p>
<p>递归，在<strong>归</strong>时让 $5 \rightarrow 4$，$4 \rightarrow 3$ …</p>
<p>首先，写一个递归方法，返回值用来拿到最后一个节点</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> ListNode <span class="title function_">reverseList</span><span class="params">(ListNode p)</span> &#123;</span><br><span class="line">    <span class="keyword">if</span> (p == <span class="literal">null</span> || p.next == <span class="literal">null</span>) &#123; <span class="comment">// 不足两个节点</span></span><br><span class="line">        <span class="keyword">return</span> p; <span class="comment">// 最后一个节点</span></span><br><span class="line">    &#125;</span><br><span class="line">    <span class="type">ListNode</span> <span class="variable">last</span> <span class="operator">=</span> reverseList(p.next);</span><br><span class="line">    <span class="keyword">return</span> last;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<ul>
<li>注意1：递归终止条件是 curr.next &#x3D;&#x3D; null，目的是到最后一个节点就结束递归，与之前递归遍历不一样</li>
<li>注意2：需要考虑空链表即 p &#x3D;&#x3D; null 的情况</li>
</ul>
<p>可以先测试一下</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="type">ListNode</span> <span class="variable">o5</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">ListNode</span>(<span class="number">5</span>, <span class="literal">null</span>);</span><br><span class="line"><span class="type">ListNode</span> <span class="variable">o4</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">ListNode</span>(<span class="number">4</span>, o5);</span><br><span class="line"><span class="type">ListNode</span> <span class="variable">o3</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">ListNode</span>(<span class="number">3</span>, o4);</span><br><span class="line"><span class="type">ListNode</span> <span class="variable">o2</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">ListNode</span>(<span class="number">2</span>, o3);</span><br><span class="line"><span class="type">ListNode</span> <span class="variable">o1</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">ListNode</span>(<span class="number">1</span>, o2);</span><br><span class="line"><span class="type">ListNode</span> <span class="variable">n1</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">E01Leetcode206</span>().reverseList(o1);</span><br><span class="line">System.out.println(n1);</span><br></pre></td></tr></table></figure>

<p>会打印</p>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">[5]</span><br></pre></td></tr></table></figure>

<p>下面为<strong>伪码</strong>调用过程，假设节点分别是 $1 \rightarrow 2 \rightarrow 3 \rightarrow 4 \rightarrow 5 \rightarrow null$，先忽略返回值</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line">reverseList(<span class="type">ListNode</span> <span class="variable">p</span> <span class="operator">=</span> <span class="number">1</span>) &#123;</span><br><span class="line">    reverseList(<span class="type">ListNode</span> <span class="variable">p</span> <span class="operator">=</span> <span class="number">2</span>) &#123;</span><br><span class="line">    	reverseList(<span class="type">ListNode</span> <span class="variable">p</span> <span class="operator">=</span> <span class="number">3</span>) &#123;</span><br><span class="line">    		reverseList(<span class="type">ListNode</span> <span class="variable">p</span> <span class="operator">=</span> <span class="number">4</span>) &#123;</span><br><span class="line">    			reverseList(<span class="type">ListNode</span> <span class="variable">p</span> <span class="operator">=</span> <span class="number">5</span>) &#123;</span><br><span class="line">    				<span class="keyword">if</span> (p == <span class="literal">null</span> || p.next == <span class="literal">null</span>) &#123;</span><br><span class="line">                        <span class="keyword">return</span> p; <span class="comment">// 返回5</span></span><br><span class="line">                    &#125;</span><br><span class="line">				&#125;</span><br><span class="line">                <span class="comment">// 此时p是4, p.next是5</span></span><br><span class="line">			&#125;</span><br><span class="line">            <span class="comment">// 此时p是3, p.next是4</span></span><br><span class="line">		&#125;</span><br><span class="line">        <span class="comment">// 此时p是2, p.next是3</span></span><br><span class="line">	&#125;</span><br><span class="line">    <span class="comment">// 此时p是1, p.next是2</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>接下来，从 p &#x3D; 4 开始，要让 $5 \rightarrow 4$，$4 \rightarrow 3$ …</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line">reverseList(<span class="type">ListNode</span> <span class="variable">p</span> <span class="operator">=</span> <span class="number">1</span>) &#123;</span><br><span class="line">    reverseList(<span class="type">ListNode</span> <span class="variable">p</span> <span class="operator">=</span> <span class="number">2</span>) &#123;</span><br><span class="line">    	reverseList(<span class="type">ListNode</span> <span class="variable">p</span> <span class="operator">=</span> <span class="number">3</span>) &#123;</span><br><span class="line">    		reverseList(<span class="type">ListNode</span> <span class="variable">p</span> <span class="operator">=</span> <span class="number">4</span>) &#123;</span><br><span class="line">    			reverseList(<span class="type">ListNode</span> <span class="variable">p</span> <span class="operator">=</span> <span class="number">5</span>) &#123;</span><br><span class="line">    				<span class="keyword">if</span> (p == <span class="literal">null</span> || p.next == <span class="literal">null</span>) &#123;</span><br><span class="line">                        <span class="keyword">return</span> p; <span class="comment">// 返回5</span></span><br><span class="line">                    &#125;</span><br><span class="line">				&#125;</span><br><span class="line">                <span class="comment">// 此时p是4, p.next是5, 要让5指向4,代码写成 p.next.next=p</span></span><br><span class="line">                <span class="comment">// 还要注意4要指向 null, 否则就死链了</span></span><br><span class="line">			&#125;</span><br><span class="line">            <span class="comment">// 此时p是3, p.next是4</span></span><br><span class="line">		&#125;</span><br><span class="line">        <span class="comment">// 此时p是2, p.next是3</span></span><br><span class="line">	&#125;</span><br><span class="line">    <span class="comment">// 此时p是1, p.next是2</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>最终代码为：</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> ListNode <span class="title function_">reverseList</span><span class="params">(ListNode p)</span> &#123;    </span><br><span class="line">    <span class="keyword">if</span> (p == <span class="literal">null</span> || p.next == <span class="literal">null</span>) &#123; <span class="comment">// 不足两个节点</span></span><br><span class="line">        <span class="keyword">return</span> p; <span class="comment">// 最后一个节点</span></span><br><span class="line">    &#125;</span><br><span class="line">    <span class="type">ListNode</span> <span class="variable">last</span> <span class="operator">=</span> reverseList(p.next);</span><br><span class="line">    p.next.next = p;</span><br><span class="line">    p.next = <span class="literal">null</span>;</span><br><span class="line">    <span class="keyword">return</span> last;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>Q：为啥不能在<strong>递</strong>的过程中倒序？</p>
<p>A：比如</p>
<ul>
<li>$ 1 \rightarrow 2 \rightarrow 3 $ 如果递的过程中让 $2 \rightarrow 1$ 那么此时 $2 \rightarrow 3$ 就被覆盖，不知道接下来递给谁</li>
<li>而归的时候让 $3 \rightarrow 2$ 不会影响上一层的 $1 \rightarrow 2$</li>
</ul>
<p>评价：单向链表没有 prev 指针，但利用递归的特性【记住了】链表每次调用时相邻两个节点是谁</p>
<p><strong>方法4</strong></p>
<p>从链表每次拿到第二个节点，将其从链表断开，插入头部，直至它为 null 结束</p>
<ol>
<li>设置指针 o1(旧头)、n1(新头)、o2(旧老二)，分别指向第一，第一，第二节点</li>
</ol>
<p>$\frac{n1 \ o1}{1} \rightarrow \frac{o2}{2} \rightarrow 3 \rightarrow 4 \rightarrow 5 \rightarrow null$</p>
<ol start="2">
<li>将 o2 节点从链表断开，即 o1 节点指向第三节点</li>
</ol>
<p>$ \frac{n1 \ o1}{1} \rightarrow 3 \rightarrow 4 \rightarrow 5 \rightarrow null$ ，$\frac{o2}{2}$</p>
<ol start="3">
<li>o2 节点链入链表头部，即</li>
</ol>
<p>$\frac{o2}{2} \rightarrow \frac{n1 \ o1}{1} \rightarrow 3 \rightarrow 4 \rightarrow 5 \rightarrow null$</p>
<ol start="4">
<li>n1 指向 o2</li>
</ol>
<p>$\frac{n1 \ o2}{2} \rightarrow \frac{o1}{1} \rightarrow 3 \rightarrow 4 \rightarrow 5 \rightarrow null$</p>
<ol start="5">
<li>o2 指向 o1 的下一个节点，即</li>
</ol>
<p>$\frac{n1}{2} \rightarrow \frac{o1}{1} \rightarrow \frac{o2}{3} \rightarrow 4 \rightarrow 5 \rightarrow null$</p>
<ol start="6">
<li><p>重复以上 $2\sim5$ 步，直到 o2 指向 null</p>
</li>
<li><p>还应当考虑边界条件，即链表中不满两个元素时，无需走以上逻辑</p>
</li>
</ol>
<p>参考答案</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> ListNode <span class="title function_">reverseList</span><span class="params">(ListNode o1)</span> &#123;    </span><br><span class="line">    <span class="keyword">if</span> (o1 == <span class="literal">null</span> || o1.next == <span class="literal">null</span>) &#123; <span class="comment">// 不足两个节点</span></span><br><span class="line">        <span class="keyword">return</span> o1;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="type">ListNode</span> <span class="variable">o2</span> <span class="operator">=</span> o1.next;</span><br><span class="line">    <span class="type">ListNode</span> <span class="variable">n1</span> <span class="operator">=</span> o1;</span><br><span class="line">    <span class="keyword">while</span> (o2 != <span class="literal">null</span>) &#123;</span><br><span class="line">        o1.next = o2.next; </span><br><span class="line">        o2.next = n1;</span><br><span class="line">        n1 = o2;</span><br><span class="line">        o2 = o1.next;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> n1;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>



<p><strong>方法5</strong></p>
<p>要点：把链表分成两部分，思路就是不断从链表2的头，往链表1的头搬移</p>
<ol>
<li>n1 指向 null，代表<strong>新链表</strong>一开始没有元素，o1 指向<strong>原链表</strong>的首节点</li>
</ol>
<p>$\frac{n1}{null}$，$\frac{o1}{1} \rightarrow 2 \rightarrow 3 \rightarrow 4 \rightarrow 5 \rightarrow null$</p>
<ol start="2">
<li>开始循环，o2 指向<strong>原链表</strong>次节点</li>
</ol>
<p>$\frac{n1}{null}$，$\frac{o1}{1} \rightarrow \frac{o2}{2} \rightarrow 3 \rightarrow 4 \rightarrow 5 \rightarrow null$</p>
<ol start="3">
<li>搬移</li>
</ol>
<p>$\frac{o1}{1} \rightarrow \frac{n1}{null}$  ， $\frac{o2}{2} \rightarrow 3 \rightarrow 4 \rightarrow 5 \rightarrow null$</p>
<ol start="4">
<li>指针复位</li>
</ol>
<p>$\frac{n1}{1} \rightarrow null$ ， $\frac{o1 \ o2}{2} \rightarrow 3 \rightarrow 4 \rightarrow 5 \rightarrow null$</p>
<ol start="5">
<li>重复 $2\sim4$ 步</li>
<li>当 o1 &#x3D; null 时退出循环</li>
</ol>
<p><strong>参考答案</strong></p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> ListNode <span class="title function_">reverseList</span><span class="params">(ListNode o1)</span> &#123;</span><br><span class="line">    <span class="keyword">if</span> (o1 == <span class="literal">null</span> || o1.next == <span class="literal">null</span>) &#123;</span><br><span class="line">        <span class="keyword">return</span> o1;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="type">ListNode</span> <span class="variable">n1</span> <span class="operator">=</span> <span class="literal">null</span>;</span><br><span class="line">    <span class="keyword">while</span> (o1 != <span class="literal">null</span>) &#123;</span><br><span class="line">        <span class="type">ListNode</span> <span class="variable">o2</span> <span class="operator">=</span> o1.next;</span><br><span class="line">        o1.next = n1;</span><br><span class="line">        n1 = o1;</span><br><span class="line">        o1 = o2;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> n1;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>评价：本质上与方法2 相同，只是方法2更为面向对象</p>
<h3 id="E02-根据值删除节点-力扣-203-题"><a href="#E02-根据值删除节点-力扣-203-题" class="headerlink" title="E02. 根据值删除节点-力扣 203 题"></a>E02. 根据值删除节点-力扣 203 题</h3><p>例如</p>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">输入：head = [1,2,6,3,6], val = 6</span><br><span class="line">输出：[1,2,3]</span><br><span class="line"></span><br><span class="line">输入：head = [], val = 1</span><br><span class="line">输出：[]</span><br><span class="line"></span><br><span class="line">输入：head = [7,7,7,7], val = 7</span><br><span class="line">输出：[]</span><br></pre></td></tr></table></figure>



<p><strong>方法1</strong></p>
<p>图中 s 代表 sentinel 哨兵（如果不加哨兵，则删除第一个节点要特殊处理），例如要删除 6</p>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">p1   p2</span><br><span class="line">s -&gt; 1 -&gt; 2 -&gt; 6 -&gt; 3 -&gt; 6 -&gt; null</span><br></pre></td></tr></table></figure>

<ul>
<li>如果 p2 不等于目标，则 p1，p2 不断后移</li>
</ul>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">	 p1   p2</span><br><span class="line">s -&gt; 1 -&gt; 2 -&gt; 6 -&gt; 3 -&gt; 6 -&gt; null</span><br><span class="line"></span><br><span class="line">	 	  p1   p2</span><br><span class="line">s -&gt; 1 -&gt; 2 -&gt; 6 -&gt; 3 -&gt; 6 -&gt; null</span><br></pre></td></tr></table></figure>

<ul>
<li>p2 &#x3D;&#x3D; 6，删除它，注意 p1 此时保持不变，p2 后移</li>
</ul>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">	 	  p1   p2</span><br><span class="line">s -&gt; 1 -&gt; 2 -&gt; 3 -&gt; 6 -&gt; null</span><br></pre></td></tr></table></figure>

<ul>
<li>p2 不等于目标，则 p1，p2 不断后移</li>
</ul>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">	 	  	   p1   p2</span><br><span class="line">s -&gt; 1 -&gt; 2 -&gt; 3 -&gt; 6 -&gt; null</span><br></pre></td></tr></table></figure>

<ul>
<li>p2 &#x3D;&#x3D; 6，删除它，注意 p1 此时保持不变，p2 后移</li>
</ul>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">	 	  	   p1   p2</span><br><span class="line">s -&gt; 1 -&gt; 2 -&gt; 3 -&gt; null</span><br></pre></td></tr></table></figure>

<ul>
<li>p2 &#x3D;&#x3D; null 退出循环</li>
</ul>
<p>最后代码</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> ListNode <span class="title function_">removeElements</span><span class="params">(ListNode head, <span class="type">int</span> val)</span> &#123;</span><br><span class="line">    <span class="type">ListNode</span> <span class="variable">sentinel</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">ListNode</span>(-<span class="number">1</span>, head);</span><br><span class="line">    <span class="type">ListNode</span> <span class="variable">p1</span> <span class="operator">=</span> sentinel;</span><br><span class="line">    ListNode p2;</span><br><span class="line">    <span class="keyword">while</span> ((p2 = p1.next) != <span class="literal">null</span>) &#123;</span><br><span class="line">        <span class="keyword">if</span> (p2.val == val) &#123;</span><br><span class="line">            p1.next = p2.next;</span><br><span class="line">        &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">            p1 = p1.next;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> sentinel.next;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>



<p><strong>方法2</strong></p>
<p>思路，递归函数负责返回：从当前节点（我）开始，完成删除的子链表</p>
<ol>
<li>若我与 v 相等，应该返回下一个节点递归结果</li>
<li>若我与 v 不等，应该返回我，但我的 next 应该更新（让我能带上后续删过的子链表）</li>
</ol>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line">removeElements(ListNode p=<span class="number">1</span>, <span class="type">int</span> v=<span class="number">6</span>)&#123;</span><br><span class="line">    <span class="number">1.</span>next=removeElements(ListNode p=<span class="number">2</span>, <span class="type">int</span> v=<span class="number">6</span>)&#123;</span><br><span class="line">    	<span class="number">2.</span>next=removeElements(ListNode p=<span class="number">6</span>, <span class="type">int</span> v=<span class="number">6</span>)&#123;</span><br><span class="line">    		removeElements(ListNode p=<span class="number">3</span>, <span class="type">int</span> v=<span class="number">6</span>)&#123;</span><br><span class="line">    			<span class="number">3.</span>next=removeElements(ListNode p=<span class="number">6</span>, <span class="type">int</span> v=<span class="number">6</span>)&#123;</span><br><span class="line">    				removeElements(ListNode p=<span class="literal">null</span>, <span class="type">int</span> v=<span class="number">6</span>)&#123;</span><br><span class="line">    					<span class="comment">// 没有节点,返回</span></span><br><span class="line">                        <span class="keyword">return</span> <span class="literal">null</span></span><br><span class="line">					&#125;</span><br><span class="line">				&#125;</span><br><span class="line">                <span class="keyword">return</span> <span class="number">3</span></span><br><span class="line">			&#125;</span><br><span class="line">		&#125;</span><br><span class="line">        <span class="keyword">return</span> <span class="number">2</span></span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> <span class="number">1</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>代码</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> ListNode <span class="title function_">removeElements</span><span class="params">(ListNode head, <span class="type">int</span> val)</span> &#123;</span><br><span class="line">    <span class="keyword">if</span> (head == <span class="literal">null</span>) &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="literal">null</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">if</span> (head.val == val) &#123;</span><br><span class="line">        <span class="keyword">return</span> removeElements(head.next, val);</span><br><span class="line">    &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">        head.next = removeElements(head.next, val);</span><br><span class="line">        <span class="keyword">return</span> head;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>



<h3 id="E03-删除倒数节点-力扣-19-题"><a href="#E03-删除倒数节点-力扣-19-题" class="headerlink" title="E03.  删除倒数节点-力扣 19 题"></a>E03.  删除倒数节点-力扣 19 题</h3><p>例如</p>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">输入：head = [1,2,3,4,5], n = 2</span><br><span class="line">输出：[1,2,3,5]</span><br><span class="line"></span><br><span class="line">输入：head = [1], n = 1</span><br><span class="line">输出：[]</span><br><span class="line"></span><br><span class="line">输入：head = [1,2], n = 1</span><br><span class="line">输出：[1]</span><br></pre></td></tr></table></figure>

<p>另外题目提示</p>
<ul>
<li>链表至少一个节点</li>
<li>n 只会在合理范围</li>
</ul>
<p><strong>方法1</strong></p>
<p>思路，写一个递归函数，用来返回下一个节点的倒数序号</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line">recursion(ListNode p=<span class="number">1</span>, <span class="type">int</span> n=<span class="number">2</span>) &#123;</span><br><span class="line">    recursion(ListNode p=<span class="number">2</span>, <span class="type">int</span> n=<span class="number">2</span>) &#123;</span><br><span class="line">    	recursion(ListNode p=<span class="number">3</span>, <span class="type">int</span> n=<span class="number">2</span>) &#123;</span><br><span class="line">    		recursion(ListNode p=<span class="number">4</span>, <span class="type">int</span> n=<span class="number">2</span>) &#123;</span><br><span class="line">    			recursion(ListNode p=<span class="number">5</span>, <span class="type">int</span> n=<span class="number">2</span>) &#123;</span><br><span class="line">    				recursion(ListNode p=<span class="literal">null</span>, <span class="type">int</span> n=<span class="number">2</span>) &#123;</span><br><span class="line">    					<span class="keyword">return</span> <span class="number">0</span>; <span class="comment">// 最内层序号0</span></span><br><span class="line">					&#125;</span><br><span class="line">                    <span class="keyword">return</span> <span class="number">1</span>; <span class="comment">// 上一次返回值+1</span></span><br><span class="line">				&#125;</span><br><span class="line">                <span class="keyword">return</span> <span class="number">2</span>;</span><br><span class="line">			&#125;</span><br><span class="line">            <span class="keyword">if</span>(返回值 == n == <span class="number">2</span>) &#123;</span><br><span class="line">                <span class="comment">// 删除 next</span></span><br><span class="line">            &#125;</span><br><span class="line">            <span class="keyword">return</span> <span class="number">3</span>;</span><br><span class="line">		&#125;</span><br><span class="line">        <span class="keyword">return</span> <span class="number">4</span>;</span><br><span class="line">	&#125;</span><br><span class="line">    <span class="keyword">return</span> <span class="number">5</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>但上述代码有一个问题，就是若删除的是第一个节点，它没有上一个节点，因此可以加一个哨兵来解决</p>
<p>代码</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> ListNode <span class="title function_">removeNthFromEnd</span><span class="params">(ListNode head, <span class="type">int</span> n)</span> &#123;</span><br><span class="line">    <span class="type">ListNode</span> <span class="variable">sentinel</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">ListNode</span>(-<span class="number">1</span>, head);</span><br><span class="line">    recursion(sentinel, n);</span><br><span class="line">    <span class="keyword">return</span> sentinel.next;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="type">int</span> <span class="title function_">recursion</span><span class="params">(ListNode p, <span class="type">int</span> n)</span> &#123;</span><br><span class="line">    <span class="keyword">if</span> (p == <span class="literal">null</span>) &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="type">int</span> <span class="variable">nth</span> <span class="operator">=</span> recursion(p.next, n);</span><br><span class="line">    <span class="keyword">if</span> (nth == n) &#123;</span><br><span class="line">        p.next = p.next.next;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> nth + <span class="number">1</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>Q：p.next.next 不怕空指针吗？</p>
<p>A：</p>
<ul>
<li>p 是待删除节点的上一个节点，如果能递归回到 p，那么 p.next 肯定有值，不会是 null</li>
<li>且题目说明了 n &gt;&#x3D;1，不会因为 nth &#x3D;&#x3D; 0 而让 p.next 指向最后的 null</li>
</ul>
<p><strong>方法2</strong></p>
<p>快慢指针，p1 指向待删节点的上一个，p2 先走 n + 1 步</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line">i=<span class="number">0</span></span><br><span class="line">p2</span><br><span class="line">s -&gt; <span class="number">1</span> -&gt; <span class="number">2</span> -&gt; <span class="number">3</span> -&gt; <span class="number">4</span> -&gt; <span class="number">5</span> -&gt; <span class="literal">null</span></span><br><span class="line"></span><br><span class="line">     i=<span class="number">1</span></span><br><span class="line">     p2</span><br><span class="line">s -&gt; <span class="number">1</span> -&gt; <span class="number">2</span> -&gt; <span class="number">3</span> -&gt; <span class="number">4</span> -&gt; <span class="number">5</span> -&gt; <span class="literal">null</span></span><br><span class="line"></span><br><span class="line">          i=<span class="number">2</span></span><br><span class="line">          p2</span><br><span class="line">s -&gt; <span class="number">1</span> -&gt; <span class="number">2</span> -&gt; <span class="number">3</span> -&gt; <span class="number">4</span> -&gt; <span class="number">5</span> -&gt; <span class="literal">null</span></span><br><span class="line"></span><br><span class="line">               i=<span class="number">3</span> 从此开始 p1 p2 依次向右平移, 直到 p2 移动到末尾</span><br><span class="line">p1             p2</span><br><span class="line">s -&gt; <span class="number">1</span> -&gt; <span class="number">2</span> -&gt; <span class="number">3</span> -&gt; <span class="number">4</span> -&gt; <span class="number">5</span> -&gt; <span class="literal">null</span></span><br><span class="line"></span><br><span class="line">               p1             p2</span><br><span class="line">s -&gt; <span class="number">1</span> -&gt; <span class="number">2</span> -&gt; <span class="number">3</span> -&gt; <span class="number">4</span> -&gt; <span class="number">5</span> -&gt; <span class="literal">null</span></span><br></pre></td></tr></table></figure>

<p>代码</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> ListNode <span class="title function_">removeNthFromEnd</span><span class="params">(ListNode head, <span class="type">int</span> n)</span> &#123;</span><br><span class="line">    <span class="type">ListNode</span> <span class="variable">s</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">ListNode</span>(-<span class="number">1</span>, head);</span><br><span class="line">    <span class="type">ListNode</span> <span class="variable">p1</span> <span class="operator">=</span> s;</span><br><span class="line">    <span class="type">ListNode</span> <span class="variable">p2</span> <span class="operator">=</span> s;</span><br><span class="line">    <span class="keyword">for</span> (<span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> <span class="number">0</span>; i &lt; n + <span class="number">1</span>; i++) &#123;</span><br><span class="line">        p2 = p2.next;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">while</span> (p2 != <span class="literal">null</span>) &#123;</span><br><span class="line">        p1 = p1.next;</span><br><span class="line">        p2 = p2.next;</span><br><span class="line">    &#125;</span><br><span class="line">    p1.next = p1.next.next;</span><br><span class="line">    <span class="keyword">return</span> s.next;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>



<p><strong>方法3</strong></p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> ListNode <span class="title function_">removeNthFromEnd</span><span class="params">(ListNode head, <span class="type">int</span> n)</span> &#123;</span><br><span class="line">    <span class="type">Composite</span> <span class="variable">c</span> <span class="operator">=</span> recursion(head, n);</span><br><span class="line">    <span class="keyword">return</span> c.node;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">static</span> <span class="keyword">class</span> <span class="title class_">Composite</span> &#123;</span><br><span class="line">    ListNode node;</span><br><span class="line">    <span class="type">int</span> nth;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="title function_">Composite</span><span class="params">(ListNode node, <span class="type">int</span> nth)</span> &#123;</span><br><span class="line">        <span class="built_in">this</span>.node = node;</span><br><span class="line">        <span class="built_in">this</span>.nth = nth;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> Composite <span class="title function_">recursion</span><span class="params">(ListNode p, <span class="type">int</span> n)</span> &#123;</span><br><span class="line">    <span class="keyword">if</span> (p == <span class="literal">null</span>) &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">new</span> <span class="title class_">Composite</span>(<span class="literal">null</span>, <span class="number">1</span>);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="type">Composite</span> <span class="variable">c</span> <span class="operator">=</span> recursion(p.next, n);</span><br><span class="line">    <span class="keyword">if</span> (c.nth != n) &#123;</span><br><span class="line">        p.next = c.node;</span><br><span class="line">        c.node = p;</span><br><span class="line">    &#125;</span><br><span class="line">    c.nth +=<span class="number">1</span>;</span><br><span class="line">    <span class="keyword">return</span> c;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>



<h3 id="E04-有序链表去重-力扣-83-题"><a href="#E04-有序链表去重-力扣-83-题" class="headerlink" title="E04. 有序链表去重-力扣 83 题"></a>E04. 有序链表去重-力扣 83 题</h3><p>例如</p>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">输入：head = [1,1,2]</span><br><span class="line">输出：[1,2]</span><br><span class="line"></span><br><span class="line">输入：head = [1,1,2,3,3]</span><br><span class="line">输出：[1,2,3]</span><br></pre></td></tr></table></figure>

<p>注意：<strong>重复元素保留一个</strong></p>
<p><strong>方法1</strong></p>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">p1   p2</span><br><span class="line">1 -&gt; 1 -&gt; 2 -&gt; 3 -&gt; 3 -&gt; null</span><br></pre></td></tr></table></figure>

<ul>
<li>p1.val &#x3D;&#x3D; p2.val 那么删除 p2，注意 p1 此时保持不变</li>
</ul>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">p1   p2</span><br><span class="line">1 -&gt; 2 -&gt; 3 -&gt; 3 -&gt; null</span><br></pre></td></tr></table></figure>

<ul>
<li>p1.val !&#x3D; p2.val 那么 p1，p2 向后移动</li>
</ul>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line">     p1   p2</span><br><span class="line"><span class="number">1</span> -&gt; <span class="number">2</span> -&gt; <span class="number">3</span> -&gt; <span class="number">3</span> -&gt; <span class="literal">null</span></span><br><span class="line">         </span><br><span class="line">          p1   p2</span><br><span class="line"><span class="number">1</span> -&gt; <span class="number">2</span> -&gt; <span class="number">3</span> -&gt; <span class="number">3</span> -&gt; <span class="literal">null</span>     </span><br></pre></td></tr></table></figure>

<ul>
<li>p1.val &#x3D;&#x3D; p2.val 那么删除 p2</li>
</ul>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">          p1   p2</span><br><span class="line">1 -&gt; 2 -&gt; 3 -&gt; null   </span><br></pre></td></tr></table></figure>

<ul>
<li>当 p2 &#x3D;&#x3D; null 退出循环</li>
</ul>
<p>代码</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> ListNode <span class="title function_">deleteDuplicates</span><span class="params">(ListNode head)</span> &#123;</span><br><span class="line">    <span class="comment">// 链表节点 &lt; 2</span></span><br><span class="line">    <span class="keyword">if</span> (head == <span class="literal">null</span> || head.next == <span class="literal">null</span>) &#123;</span><br><span class="line">        <span class="keyword">return</span> head;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">// 链表节点 &gt;= 2</span></span><br><span class="line">    <span class="type">ListNode</span> <span class="variable">p1</span> <span class="operator">=</span> head;</span><br><span class="line">    ListNode p2;</span><br><span class="line">    <span class="keyword">while</span> ((p2 = p1.next) != <span class="literal">null</span>) &#123;</span><br><span class="line">        <span class="keyword">if</span> (p1.val == p2.val) &#123;</span><br><span class="line">            p1.next = p2.next;</span><br><span class="line">        &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">            p1 = p1.next;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> head;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>



<p><strong>方法2</strong></p>
<p>递归函数负责返回：从当前节点（我）开始，完成去重的链表</p>
<ol>
<li>若我与 next 重复，返回 next</li>
<li>若我与 next 不重复，返回我，但 next 应当更新</li>
</ol>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line">deleteDuplicates(ListNode p=<span class="number">1</span>) &#123;</span><br><span class="line">    deleteDuplicates(ListNode p=<span class="number">1</span>) &#123;</span><br><span class="line">        <span class="number">1.</span>next=deleteDuplicates(ListNode p=<span class="number">2</span>) &#123;</span><br><span class="line">            <span class="number">2.</span>next=deleteDuplicates(ListNode p=<span class="number">3</span>) &#123;</span><br><span class="line">                deleteDuplicates(ListNode p=<span class="number">3</span>) &#123;</span><br><span class="line">					<span class="comment">// 只剩一个节点，返回</span></span><br><span class="line">                    <span class="keyword">return</span> <span class="number">3</span></span><br><span class="line">                &#125;                </span><br><span class="line">            &#125;</span><br><span class="line">            <span class="keyword">return</span> <span class="number">2</span></span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> <span class="number">1</span></span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>代码</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> ListNode <span class="title function_">deleteDuplicates</span><span class="params">(ListNode p)</span> &#123;</span><br><span class="line">    <span class="keyword">if</span> (p == <span class="literal">null</span> || p.next == <span class="literal">null</span>) &#123;</span><br><span class="line">        <span class="keyword">return</span> p;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">if</span>(p.val == p.next.val) &#123;</span><br><span class="line">        <span class="keyword">return</span> deleteDuplicates(p.next);</span><br><span class="line">    &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">        p.next = deleteDuplicates(p.next);</span><br><span class="line">        <span class="keyword">return</span> p;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>



<h3 id="E05-有序链表去重-力扣-82-题"><a href="#E05-有序链表去重-力扣-82-题" class="headerlink" title="E05. 有序链表去重-力扣 82 题"></a>E05. 有序链表去重-力扣 82 题</h3><p>例如</p>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">输入：head = [1,2,3,3,4,4,5]</span><br><span class="line">输出：[1,2,5]</span><br><span class="line"></span><br><span class="line">输入：head = [1,1,1,2,3]</span><br><span class="line">输出：[2,3]</span><br></pre></td></tr></table></figure>

<p>注意：<strong>重复元素一个不留</strong></p>
<p><strong>方法1</strong></p>
<p>递归函数负责返回：从当前节点（我）开始，完成去重的链表</p>
<ol>
<li>若我与 next 重复，一直找到下一个不重复的节点，以它的返回结果为准</li>
<li>若我与 next 不重复，返回我，同时更新 next</li>
</ol>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line">deleteDuplicates(<span class="type">ListNode</span> <span class="variable">p</span> <span class="operator">=</span> <span class="number">1</span>) &#123;</span><br><span class="line">    <span class="comment">// 找下个不重复的</span></span><br><span class="line">	deleteDuplicates(<span class="type">ListNode</span> <span class="variable">p</span> <span class="operator">=</span> <span class="number">1</span>) &#123;</span><br><span class="line">        deleteDuplicates(<span class="type">ListNode</span> <span class="variable">p</span> <span class="operator">=</span> <span class="number">1</span>) &#123;</span><br><span class="line">			deleteDuplicates(<span class="type">ListNode</span> <span class="variable">p</span> <span class="operator">=</span> <span class="number">2</span>) &#123;</span><br><span class="line">                <span class="number">2.</span>next=deleteDuplicates(<span class="type">ListNode</span> <span class="variable">p</span> <span class="operator">=</span> <span class="number">3</span>) &#123;</span><br><span class="line">					<span class="comment">// 只剩一个节点，返回</span></span><br><span class="line">                    <span class="keyword">return</span> <span class="number">3</span></span><br><span class="line">                &#125;</span><br><span class="line">                <span class="keyword">return</span> <span class="number">2</span></span><br><span class="line">			&#125;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>代码</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> ListNode <span class="title function_">deleteDuplicates</span><span class="params">(ListNode p)</span> &#123;</span><br><span class="line">    <span class="keyword">if</span> (p == <span class="literal">null</span> || p.next == <span class="literal">null</span>) &#123;</span><br><span class="line">        <span class="keyword">return</span> p;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">if</span> (p.val == p.next.val) &#123;</span><br><span class="line">        <span class="type">ListNode</span> <span class="variable">x</span> <span class="operator">=</span> p.next.next;</span><br><span class="line">        <span class="keyword">while</span> (x != <span class="literal">null</span> &amp;&amp; x.val == p.val) &#123;</span><br><span class="line">            x = x.next;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> deleteDuplicates(x);</span><br><span class="line">    &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">        p.next = deleteDuplicates(p.next);</span><br><span class="line">        <span class="keyword">return</span> p;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>



<p><strong>方法2</strong></p>
<p>p1 是待删除的上一个节点，每次循环对比 p2、p3 的值</p>
<ul>
<li>如果 p2 与 p3 的值重复，那么 p3 继续后移，直到找到与 p2 不重复的节点，p1 指向 p3 完成删除</li>
<li>如果 p2 与 p3 的值不重复，p1，p2，p3 向后平移一位，继续上面的操作</li>
<li>p2 或 p3 为 null 退出循环<ul>
<li>p2 为 null 的情况，比如链表为 1 1 1 null</li>
</ul>
</li>
</ul>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">p1 p2 p3</span><br><span class="line">s, 1, 1, 1, 2, 3, null</span><br><span class="line"></span><br><span class="line">p1 p2    p3</span><br><span class="line">s, 1, 1, 1, 2, 3, null</span><br><span class="line"></span><br><span class="line">p1 p2       p3</span><br><span class="line">s, 1, 1, 1, 2, 3, null</span><br><span class="line"></span><br><span class="line">p1 p3</span><br><span class="line">s, 2, 3, null</span><br><span class="line"></span><br><span class="line">p1 p2 p3</span><br><span class="line">s, 2, 3, null</span><br><span class="line"></span><br><span class="line">   p1 p2 p3</span><br><span class="line">s, 2, 3, null</span><br></pre></td></tr></table></figure>



<p>代码</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> ListNode <span class="title function_">deleteDuplicates</span><span class="params">(ListNode head)</span> &#123;</span><br><span class="line">    <span class="keyword">if</span> (head == <span class="literal">null</span> || head.next == <span class="literal">null</span>) &#123;</span><br><span class="line">        <span class="keyword">return</span> head;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="type">ListNode</span> <span class="variable">s</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">ListNode</span>(-<span class="number">1</span>, head);</span><br><span class="line">    <span class="type">ListNode</span> <span class="variable">p1</span> <span class="operator">=</span> s;</span><br><span class="line">    ListNode p2;</span><br><span class="line">    ListNode p3;</span><br><span class="line">    <span class="keyword">while</span> ((p2 = p1.next) != <span class="literal">null</span> &amp;&amp; (p3 = p2.next) != <span class="literal">null</span>) &#123;</span><br><span class="line">        <span class="keyword">if</span> (p2.val == p3.val) &#123;</span><br><span class="line">            <span class="keyword">while</span> ((p3 = p3.next) != <span class="literal">null</span> </span><br><span class="line">                   &amp;&amp; p3.val == p2.val) &#123;</span><br><span class="line">            &#125;</span><br><span class="line">            p1.next = p3;</span><br><span class="line">        &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">            p1 = p1.next;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> s.next;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>



<h3 id="E06-合并有序链表-力扣-21-题"><a href="#E06-合并有序链表-力扣-21-题" class="headerlink" title="E06. 合并有序链表-力扣 21 题"></a>E06. 合并有序链表-力扣 21 题</h3><p>例</p>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">输入：l1 = [1,2,4], l2 = [1,3,4]</span><br><span class="line">输出：[1,1,2,3,4,4]</span><br><span class="line">    </span><br><span class="line">输入：l1 = [], l2 = []</span><br><span class="line">输出：[]</span><br><span class="line"></span><br><span class="line">输入：l1 = [], l2 = [0]</span><br><span class="line">输出：[0]</span><br></pre></td></tr></table></figure>

<p><strong>方法1</strong></p>
<ul>
<li>谁小，把谁链给 p，p 和小的都向后平移一位</li>
<li>当 p1、p2 有一个为 null，退出循环，把不为 null 的链给 p</li>
</ul>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">p1</span><br><span class="line">1	3	8	9	null</span><br><span class="line"></span><br><span class="line">p2</span><br><span class="line">2	4	null</span><br><span class="line"></span><br><span class="line">p		</span><br><span class="line">s	null</span><br></pre></td></tr></table></figure>

<p>代码</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> ListNode <span class="title function_">mergeTwoLists</span><span class="params">(ListNode p1, ListNode p2)</span> &#123;</span><br><span class="line">    <span class="type">ListNode</span> <span class="variable">s</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">ListNode</span>(-<span class="number">1</span>, <span class="literal">null</span>);</span><br><span class="line">    <span class="type">ListNode</span> <span class="variable">p</span> <span class="operator">=</span> s;</span><br><span class="line">    <span class="keyword">while</span> (p1 != <span class="literal">null</span> &amp;&amp; p2 != <span class="literal">null</span>) &#123;</span><br><span class="line">        <span class="keyword">if</span> (p1.val &lt; p2.val) &#123;</span><br><span class="line">            p.next = p1;</span><br><span class="line">            p1 = p1.next;</span><br><span class="line">        &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">            p.next = p2;</span><br><span class="line">            p2 = p2.next;</span><br><span class="line">        &#125;</span><br><span class="line">        p = p.next;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">if</span> (p1 != <span class="literal">null</span>) &#123;</span><br><span class="line">        p.next = p1;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">if</span> (p2 != <span class="literal">null</span>) &#123;</span><br><span class="line">        p.next = p2;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> s.next;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<ul>
<li>可以自行验证<strong>例</strong>中后两种情况</li>
</ul>
<p><strong>方法2</strong></p>
<p>递归函数应该返回</p>
<ul>
<li>更小的那个链表节点，并把它剩余节点与另一个链表再次递归</li>
<li>返回之前，更新此节点的 next</li>
</ul>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line">mergeTwoLists(p1=[<span class="number">1</span>,<span class="number">3</span>,<span class="number">8</span>,<span class="number">9</span>], p2=[<span class="number">2</span>,<span class="number">4</span>]) &#123;</span><br><span class="line">    <span class="number">1.</span>next=mergeTwoLists(p1=[<span class="number">3</span>,<span class="number">8</span>,<span class="number">9</span>], p2=[<span class="number">2</span>,<span class="number">4</span>]) &#123;</span><br><span class="line">        <span class="number">2.</span>next=mergeTwoLists(p1=[<span class="number">3</span>,<span class="number">8</span>,<span class="number">9</span>], p2=[<span class="number">4</span>]) &#123;            </span><br><span class="line">            <span class="number">3.</span>next=mergeTwoLists(p1=[<span class="number">8</span>,<span class="number">9</span>], p2=[<span class="number">4</span>]) &#123;</span><br><span class="line">                <span class="number">4.</span>next=mergeTwoLists(p1=[<span class="number">8</span>,<span class="number">9</span>], p2=<span class="literal">null</span>) &#123;</span><br><span class="line">                    <span class="keyword">return</span> [<span class="number">8</span>,<span class="number">9</span>]</span><br><span class="line">                &#125;</span><br><span class="line">                <span class="keyword">return</span> <span class="number">4</span></span><br><span class="line">            &#125;</span><br><span class="line">            <span class="keyword">return</span> <span class="number">3</span></span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> <span class="number">2</span></span><br><span class="line">    &#125;</span><br><span class="line">	<span class="keyword">return</span> <span class="number">1</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>



<h3 id="E07-合并多个有序链表-力扣-23-题"><a href="#E07-合并多个有序链表-力扣-23-题" class="headerlink" title="E07. 合并多个有序链表-力扣 23 题"></a>E07. 合并多个有序链表-力扣 23 题</h3><p>例</p>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">输入：lists = [[1,4,5],[1,3,4],[2,6]]</span><br><span class="line">输出：[1,1,2,3,4,4,5,6]</span><br><span class="line">解释：链表数组如下：</span><br><span class="line">[</span><br><span class="line">  1-&gt;4-&gt;5,</span><br><span class="line">  1-&gt;3-&gt;4,</span><br><span class="line">  2-&gt;6</span><br><span class="line">]</span><br><span class="line">将它们合并到一个有序链表中得到。</span><br><span class="line">1-&gt;1-&gt;2-&gt;3-&gt;4-&gt;4-&gt;5-&gt;6</span><br></pre></td></tr></table></figure>



<p><strong>方法1</strong></p>
<p>递归</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> ListNode <span class="title function_">mergeKLists</span><span class="params">(ListNode[] lists)</span> &#123;</span><br><span class="line">    <span class="keyword">if</span> (lists.length == <span class="number">0</span>) &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="literal">null</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> merge(lists, <span class="number">0</span>, lists.length - <span class="number">1</span>);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> ListNode <span class="title function_">split</span><span class="params">(ListNode[] lists, <span class="type">int</span> i, <span class="type">int</span> j)</span> &#123;</span><br><span class="line">    System.out.println(i + <span class="string">&quot; &quot;</span> + j);</span><br><span class="line">    <span class="keyword">if</span> (j == i) &#123;</span><br><span class="line">        <span class="keyword">return</span> lists[i];</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="type">int</span> <span class="variable">m</span> <span class="operator">=</span> (i + j) &gt;&gt;&gt; <span class="number">1</span>;</span><br><span class="line">    <span class="keyword">return</span> mergeTwoLists(</span><br><span class="line">        split(lists, i, m),</span><br><span class="line">        split(lists, m + <span class="number">1</span>, j)</span><br><span class="line">    );</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>还可以用优先级队列求解，这个放在后面讲</p>
<h3 id="E08-查找链表中间节点-力扣-876-题"><a href="#E08-查找链表中间节点-力扣-876-题" class="headerlink" title="E08. 查找链表中间节点-力扣 876 题"></a>E08. 查找链表中间节点-力扣 876 题</h3><p>例如</p>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">输入：[1,2,3,4,5]</span><br><span class="line">输出：此列表中的结点 3 (序列化形式：[3,4,5])</span><br><span class="line"></span><br><span class="line">输入：[1,2,3,4,5,6]</span><br><span class="line">输出：此列表中的结点 4 (序列化形式：[4,5,6])</span><br></pre></td></tr></table></figure>

<ul>
<li><strong>偶数</strong>节点时，中间点是靠右的那个</li>
</ul>
<p>解法：快慢指针，快指针一次走两步，慢指针一次走一步，当快指针到链表结尾时，慢指针恰好走到链表的一半</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> ListNode <span class="title function_">middleNode</span><span class="params">(ListNode head)</span> &#123;</span><br><span class="line">    <span class="type">ListNode</span> <span class="variable">p1</span> <span class="operator">=</span> head;	<span class="comment">// 慢指针，中间点</span></span><br><span class="line">    <span class="type">ListNode</span> <span class="variable">p2</span> <span class="operator">=</span> head;	<span class="comment">// 快指针</span></span><br><span class="line">    <span class="keyword">while</span> (p2 != <span class="literal">null</span> &amp;&amp; p2.next != <span class="literal">null</span>) &#123;</span><br><span class="line">        p1 = p1.next;</span><br><span class="line">        p2 = p2.next;</span><br><span class="line">        p2 = p2.next;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> p1;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>



<h3 id="E09-回文链表-力扣-234-题"><a href="#E09-回文链表-力扣-234-题" class="headerlink" title="E09. 回文链表-力扣 234 题"></a>E09. 回文链表-力扣 234 题</h3><p>所谓回文指正着读、反着读，结果一样，例如</p>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">[1,2,2,1]</span><br><span class="line">[1,2,3,2,1]</span><br></pre></td></tr></table></figure>

<p>它们都是回文链表，不是回文的例子</p>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">[1,2,3,1]  --反过来--&gt;  [1,3,2,1]</span><br></pre></td></tr></table></figure>



<p>解法</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="comment">/*</span></span><br><span class="line"><span class="comment">    步骤1. 找中间点</span></span><br><span class="line"><span class="comment">    步骤2. 中间点后半个链表反转</span></span><br><span class="line"><span class="comment">    步骤3. 反转后链表与原链表逐一比较</span></span><br><span class="line"><span class="comment">*/</span></span><br><span class="line"><span class="keyword">public</span> <span class="type">boolean</span> <span class="title function_">isPalindrome</span><span class="params">(ListNode head)</span> &#123;</span><br><span class="line">    <span class="type">ListNode</span> <span class="variable">middle</span> <span class="operator">=</span> middle(head);</span><br><span class="line">    <span class="type">ListNode</span> <span class="variable">newHead</span> <span class="operator">=</span> reverse(middle);</span><br><span class="line">    <span class="keyword">while</span> (newHead != <span class="literal">null</span>) &#123;</span><br><span class="line">        <span class="keyword">if</span> (newHead.val != head.val) &#123;</span><br><span class="line">            <span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        newHead = newHead.next;</span><br><span class="line">        head = head.next;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> <span class="literal">true</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">private</span> ListNode <span class="title function_">reverse</span><span class="params">(ListNode o1)</span> &#123;</span><br><span class="line">    <span class="type">ListNode</span> <span class="variable">n1</span> <span class="operator">=</span> <span class="literal">null</span>;</span><br><span class="line">    <span class="keyword">while</span> (o1 != <span class="literal">null</span>) &#123;</span><br><span class="line">        <span class="type">ListNode</span> <span class="variable">o2</span> <span class="operator">=</span> o1.next;</span><br><span class="line">        o1.next = n1;</span><br><span class="line">        n1 = o1;</span><br><span class="line">        o1 = o2;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> n1;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">private</span> ListNode <span class="title function_">middle</span><span class="params">(ListNode head)</span> &#123;</span><br><span class="line">    <span class="type">ListNode</span> <span class="variable">p1</span> <span class="operator">=</span> head; <span class="comment">// 慢</span></span><br><span class="line">    <span class="type">ListNode</span> <span class="variable">p2</span> <span class="operator">=</span> head; <span class="comment">// 快</span></span><br><span class="line">    <span class="keyword">while</span> (p2 != <span class="literal">null</span> &amp;&amp; p2.next != <span class="literal">null</span>) &#123;</span><br><span class="line">        p1 = p1.next;</span><br><span class="line">        p2 = p2.next.next;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> p1;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>



<p>优化后解法</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="type">boolean</span> <span class="title function_">isPalindrome</span><span class="params">(ListNode h1)</span> &#123;</span><br><span class="line">    <span class="keyword">if</span> (h1 == <span class="literal">null</span> || h1.next == <span class="literal">null</span>) &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="literal">true</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="type">ListNode</span> <span class="variable">p1</span> <span class="operator">=</span> h1; 	<span class="comment">// 慢指针，中间点</span></span><br><span class="line">    <span class="type">ListNode</span> <span class="variable">p2</span> <span class="operator">=</span> h1; 	<span class="comment">// 快指针</span></span><br><span class="line">    <span class="type">ListNode</span> <span class="variable">n1</span> <span class="operator">=</span> <span class="literal">null</span>;	<span class="comment">// 新头</span></span><br><span class="line">    <span class="type">ListNode</span> <span class="variable">o1</span> <span class="operator">=</span> h1;	<span class="comment">// 旧头</span></span><br><span class="line">    <span class="comment">// 快慢指针找中间点</span></span><br><span class="line">    <span class="keyword">while</span> (p2 != <span class="literal">null</span> &amp;&amp; p2.next != <span class="literal">null</span>) &#123;</span><br><span class="line">        p1 = p1.next;</span><br><span class="line">        p2 = p2.next.next;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 反转前半部分</span></span><br><span class="line">        o1.next = n1;</span><br><span class="line">        n1 = o1;</span><br><span class="line">        o1 = p1;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">if</span> (p2 != <span class="literal">null</span>) &#123; <span class="comment">// 节点数为奇数</span></span><br><span class="line">        p1 = p1.next;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">// 同步比较新头和后半部分</span></span><br><span class="line">    <span class="keyword">while</span> (n1 != <span class="literal">null</span>) &#123;</span><br><span class="line">        <span class="keyword">if</span> (n1.val != p1.val) &#123;</span><br><span class="line">            <span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        p1 = p1.next;</span><br><span class="line">        n1 = n1.next;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> <span class="literal">true</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>



<h3 id="E10-环形链表-力扣-141-题"><a href="#E10-环形链表-力扣-141-题" class="headerlink" title="E10. 环形链表-力扣 141 题"></a>E10. 环形链表-力扣 141 题</h3><p>本题以及下题，实际是 Floyd’s Tortoise and Hare Algorithm （Floyd 龟兔赛跑算法）[^15]</p>
<blockquote>
<p>除了 Floyd 判环算法外，还有其它的判环算法，详见 <a target="_blank" rel="noopener" href="https://en.wikipedia.org/wiki/Cycle_detection">https://en.wikipedia.org/wiki/Cycle_detection</a></p>
</blockquote>
<img src= "" data-lazy-src="http://rx6zk4j2b.hn-bkt.clouddn.com/blogs/image-20221229190646563.png" alt="image-20221229190646563" style="zoom: 50%;" />

<p>如果链表上存在环，那么在环上以不同速度前进的两个指针必定会在某个时刻相遇。算法分为两个阶段</p>
<p>阶段1</p>
<ul>
<li>龟一次走一步，兔子一次走两步</li>
<li>当兔子能走到终点时，不存在环</li>
<li>当兔子能追上龟时，可以判断存在环</li>
</ul>
<p>阶段2</p>
<ul>
<li>从它们第一次相遇开始，龟回到起点，兔子保持原位不变</li>
<li>龟和兔子一次都走一步</li>
<li>当再次相遇时，地点就是环的入口</li>
</ul>
<p>为什么呢？</p>
<ul>
<li>设起点到入口走 a 步（本例是 7），绕环一圈长度为 b（本例是 5），</li>
<li>那么<strong>从起点开始，走 a + 绕环 n 圈，都能找到环入口</strong></li>
<li>第一次相遇时<ul>
<li>兔走了 a + 绕环 n 圈（本例 2 圈） + k，k 是它们相遇距环入口位置（本例 3，不重要）</li>
<li>龟走了 a + 绕环 n 圈（本例 0 圈） + k，当然它绕的圈数比兔少</li>
<li>兔走的距离是龟的两倍，所以<strong>龟走的</strong> &#x3D; 兔走的 - 龟走的 &#x3D; <strong>绕环 n 圈</strong></li>
</ul>
</li>
<li>而前面分析过，如果走 a + 绕环 n 圈，都能找到环入口，因此从相遇点开始，再走 a 步，就是环入口</li>
</ul>
<p>阶段1 参考代码（判断是否有环）</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="type">boolean</span> <span class="title function_">hasCycle</span><span class="params">(ListNode head)</span> &#123;</span><br><span class="line">    <span class="type">ListNode</span> <span class="variable">h</span> <span class="operator">=</span> head; <span class="comment">// 兔</span></span><br><span class="line">    <span class="type">ListNode</span> <span class="variable">t</span> <span class="operator">=</span> head; <span class="comment">// 龟</span></span><br><span class="line">    <span class="keyword">while</span> (h != <span class="literal">null</span> &amp;&amp; h.next != <span class="literal">null</span>) &#123;</span><br><span class="line">        t = t.next;</span><br><span class="line">        h = h.next.next;</span><br><span class="line">        <span class="keyword">if</span>(h == t)&#123;</span><br><span class="line">            <span class="keyword">return</span> <span class="literal">true</span>;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>



<h3 id="E11-环形链表-力扣-142-题"><a href="#E11-环形链表-力扣-142-题" class="headerlink" title="E11. 环形链表-力扣 142 题"></a>E11. 环形链表-力扣 142 题</h3><p>阶段2 参考代码（找到环入口）</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> ListNode <span class="title function_">detectCycle</span><span class="params">(ListNode head)</span> &#123;</span><br><span class="line">    <span class="type">ListNode</span> <span class="variable">t</span> <span class="operator">=</span> head; <span class="comment">// 龟</span></span><br><span class="line">    <span class="type">ListNode</span> <span class="variable">h</span> <span class="operator">=</span> head; <span class="comment">// 兔</span></span><br><span class="line">    <span class="keyword">while</span> (h != <span class="literal">null</span> &amp;&amp; h.next != <span class="literal">null</span>) &#123;</span><br><span class="line">        t = t.next;</span><br><span class="line">        h = h.next.next;</span><br><span class="line">        <span class="keyword">if</span> (h == t) &#123;</span><br><span class="line">            t = head;</span><br><span class="line">            <span class="keyword">while</span> (<span class="literal">true</span>) &#123;</span><br><span class="line">                <span class="keyword">if</span> (h == t) &#123;</span><br><span class="line">                    <span class="keyword">return</span> h;</span><br><span class="line">                &#125;</span><br><span class="line">                h = h.next;</span><br><span class="line">                t = t.next;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> <span class="literal">null</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<ul>
<li>还有一道扩展题目，也可以用判环算法思想来解：就是 287 题，寻找重复数</li>
</ul>
<h3 id="Ex1-删除节点-力扣-237-题"><a href="#Ex1-删除节点-力扣-237-题" class="headerlink" title="Ex1. 删除节点-力扣 237 题"></a>Ex1. 删除节点-力扣 237 题</h3><p>这道题目比较简单，留给大家自己练习</p>
<p>例如</p>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">输入：head = [4,5,1,9], node = 5</span><br><span class="line">输出：[4,1,9]</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">输入：head = [4,5,1,9], node = 1</span><br><span class="line">输出：[4,5,9]</span><br></pre></td></tr></table></figure>

<p>注意：被删除的节点<strong>不是</strong>末尾节点</p>
<p><strong>参考答案</strong></p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Ex1Leetcode237</span> &#123;</span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     *</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> node 待删除节点, 题目已说明肯定不是最后一个节点</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">deleteNode</span><span class="params">(ListNode node)</span> &#123;</span><br><span class="line">        node.val = node.next.val;		<span class="comment">// 下一个节点值赋值给待&quot;删除&quot;节点</span></span><br><span class="line">        node.next = node.next.next;		<span class="comment">// 把下一个节点删除</span></span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> &#123;</span><br><span class="line">        <span class="type">ListNode</span> <span class="variable">o5</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">ListNode</span>(<span class="number">5</span>, <span class="literal">null</span>);</span><br><span class="line">        <span class="type">ListNode</span> <span class="variable">o4</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">ListNode</span>(<span class="number">4</span>, o5);</span><br><span class="line">        <span class="type">ListNode</span> <span class="variable">o3</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">ListNode</span>(<span class="number">3</span>, o4);</span><br><span class="line">        <span class="type">ListNode</span> <span class="variable">o2</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">ListNode</span>(<span class="number">2</span>, o3);</span><br><span class="line">        <span class="type">ListNode</span> <span class="variable">o1</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">ListNode</span>(<span class="number">1</span>, o2);</span><br><span class="line">        System.out.println(o1);</span><br><span class="line">        <span class="keyword">new</span> <span class="title class_">E0xLeetcode237</span>().deleteNode(o3);</span><br><span class="line">        System.out.println(o1);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>输出</p>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">[1,2,3,4,5]</span><br><span class="line">[1,2,4,5]</span><br></pre></td></tr></table></figure>



<h3 id="Ex2-共尾链表-力扣-160-题"><a href="#Ex2-共尾链表-力扣-160-题" class="headerlink" title="Ex2. 共尾链表-力扣 160 题"></a>Ex2. 共尾链表-力扣 160 题</h3><p>原题叫做<strong>相交</strong>链表，个人觉得用<strong>共尾</strong>链表更形象些，此题更像是一道脑筋急转弯，留给大家练习</p>
<p>例如，下图的两个链表 [1, 2, 4, 5] 与 [3, 4, 5] 它们中 [4, 5] 是相同的，此时应返回节点 4</p>
<p><img src= "" data-lazy-src="http://rx6zk4j2b.hn-bkt.clouddn.com/blogs/image-20221228081715799.png" alt="image-20221228081715799"></p>
<p>非共尾的情况，如下图所示，此时返回 null</p>
<p><img src= "" data-lazy-src="http://rx6zk4j2b.hn-bkt.clouddn.com/blogs/image-20221228082002730.png" alt="image-20221228082002730"></p>
<p>思路，称两个链表为 a&#x3D;[1, 2, 4, 5]，b&#x3D;[3, 4, 5]，图中用 N 代表 null</p>
<ol>
<li>遍历 a，遇到 null 时改道遍历 b</li>
<li>与此同时，遍历 b，遇到 null 时改道遍历 a</li>
<li>在此过程中，如果<strong>遇到相同</strong>的节点，即为找寻目标，返回即可，如下图中的第二次出现的 4</li>
<li>相同节点应该比较其<strong>引用值</strong>，图中数字只是为了便于区分</li>
</ol>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">1	2	4	5	N	3	4	5	N</span><br><span class="line">3	4	5	N	1	2	4	5	N</span><br></pre></td></tr></table></figure>

<p>如果两个链表长度相同，则可以更早找到目标，例如 a&#x3D;[1, 4, 5]，b&#x3D;[3, 4, 5]，第一次出现 4 时，即可返回</p>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">1	4	5	N	3	4	5	N</span><br><span class="line">3	4	5	N	1	4	5	N</span><br></pre></td></tr></table></figure>

<p>如果是非共尾的情况，如 a&#x3D;[1, 2, 4]，b&#x3D;[3, 5]，可以看到，唯一相等的情况，是遍历到最后那个 N 此时退出循环</p>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">1	2	4	N	3	5	N</span><br><span class="line">3	5	N	1	2	4	N</span><br></pre></td></tr></table></figure>

<p>代码</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> ListNode <span class="title function_">getIntersectionNode</span><span class="params">(ListNode a, ListNode b)</span> &#123;</span><br><span class="line">    <span class="type">ListNode</span> <span class="variable">p1</span> <span class="operator">=</span> a;</span><br><span class="line">    <span class="type">ListNode</span> <span class="variable">p2</span> <span class="operator">=</span> b;</span><br><span class="line">    <span class="keyword">while</span> (<span class="literal">true</span>) &#123;</span><br><span class="line">        <span class="keyword">if</span> (p1 == p2) &#123;</span><br><span class="line">            <span class="keyword">return</span> p1;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">if</span> (p1 == <span class="literal">null</span>) &#123;</span><br><span class="line">            p1 = b;</span><br><span class="line">        &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">            p1 = p1.next;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">if</span> (p2 == <span class="literal">null</span>) &#123;</span><br><span class="line">            p2 = a;</span><br><span class="line">        &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">            p2 = p2.next;</span><br><span class="line">        &#125;            </span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>



<h2 id="3-6-数组"><a href="#3-6-数组" class="headerlink" title="3.6 数组"></a>3.6 数组</h2><h3 id="E01-合并有序数组"><a href="#E01-合并有序数组" class="headerlink" title="E01. 合并有序数组"></a>E01. 合并有序数组</h3><p>将数组内两个区间内的有序元素合并</p>
<p>例</p>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">[1, 5, 6, 2, 4, 10, 11]</span><br></pre></td></tr></table></figure>

<p>可以视作两个有序区间</p>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">[1, 5, 6] 和 [2, 4, 10, 11]</span><br></pre></td></tr></table></figure>

<p>合并后，结果仍存储于原有空间</p>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">[1, 2, 4, 5, 6, 10, 11]</span><br></pre></td></tr></table></figure>



<p><strong>方法1</strong></p>
<p>递归</p>
<ul>
<li>每次递归把更小的元素复制到结果数组</li>
</ul>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line">merge(left=[<span class="number">1</span>,<span class="number">5</span>,<span class="number">6</span>],right=[<span class="number">2</span>,<span class="number">4</span>,<span class="number">10</span>,<span class="number">11</span>],a2=[])&#123;</span><br><span class="line">    merge(left=[<span class="number">5</span>,<span class="number">6</span>],right=[<span class="number">2</span>,<span class="number">4</span>,<span class="number">10</span>,<span class="number">11</span>],a2=[<span class="number">1</span>])&#123;</span><br><span class="line">        merge(left=[<span class="number">5</span>,<span class="number">6</span>],right=[<span class="number">4</span>,<span class="number">10</span>,<span class="number">11</span>],a2=[<span class="number">1</span>,<span class="number">2</span>])&#123;</span><br><span class="line">            merge(left=[<span class="number">5</span>,<span class="number">6</span>],right=[<span class="number">10</span>,<span class="number">11</span>],a2=[<span class="number">1</span>,<span class="number">2</span>,<span class="number">4</span>])&#123;</span><br><span class="line">                merge(left=[<span class="number">6</span>],right=[<span class="number">10</span>,<span class="number">11</span>],a2=[<span class="number">1</span>,<span class="number">2</span>,<span class="number">4</span>,<span class="number">5</span>])&#123;</span><br><span class="line">                    merge(left=[],right=[<span class="number">10</span>,<span class="number">11</span>],a2=[<span class="number">1</span>,<span class="number">2</span>,<span class="number">4</span>,<span class="number">5</span>,<span class="number">6</span>])&#123;</span><br><span class="line">						<span class="comment">// 拷贝10，11</span></span><br><span class="line">                    &#125;</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>代码</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">merge</span><span class="params">(<span class="type">int</span>[] a1, <span class="type">int</span> i, <span class="type">int</span> iEnd, <span class="type">int</span> j, <span class="type">int</span> jEnd,</span></span><br><span class="line"><span class="params">                              <span class="type">int</span>[] a2, <span class="type">int</span> k)</span> &#123;</span><br><span class="line">    <span class="keyword">if</span> (i &gt; iEnd) &#123;</span><br><span class="line">        System.arraycopy(a1, j, a2, k, jEnd - j + <span class="number">1</span>);</span><br><span class="line">        <span class="keyword">return</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">if</span> (j &gt; jEnd) &#123;</span><br><span class="line">        System.arraycopy(a1, i, a2, k, iEnd - i + <span class="number">1</span>);</span><br><span class="line">        <span class="keyword">return</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">if</span> (a1[i] &lt; a1[j]) &#123;</span><br><span class="line">        a2[k] = a1[i];</span><br><span class="line">        merge(a1, i + <span class="number">1</span>, iEnd, j, jEnd, a2, k + <span class="number">1</span>);</span><br><span class="line">    &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">        a2[k] = a1[j];</span><br><span class="line">        merge(a1, i, iEnd, j + <span class="number">1</span>, jEnd, a2, k + <span class="number">1</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>测试</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="type">int</span>[] a1 = &#123;<span class="number">1</span>, <span class="number">5</span>, <span class="number">6</span>, <span class="number">2</span>, <span class="number">4</span>, <span class="number">10</span>, <span class="number">11</span>&#125;;</span><br><span class="line"><span class="type">int</span>[] a2 = <span class="keyword">new</span> <span class="title class_">int</span>[a1.length];</span><br><span class="line">merge(a1, <span class="number">0</span>, <span class="number">2</span>, <span class="number">3</span>, <span class="number">6</span>, a2, <span class="number">0</span>);</span><br></pre></td></tr></table></figure>



<p><strong>方法2</strong></p>
<p>代码</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">merge</span><span class="params">(<span class="type">int</span>[] a1, <span class="type">int</span> i, <span class="type">int</span> iEnd,</span></span><br><span class="line"><span class="params">                             <span class="type">int</span> j, <span class="type">int</span> jEnd,</span></span><br><span class="line"><span class="params">                             <span class="type">int</span>[] a2)</span> &#123;</span><br><span class="line">    <span class="type">int</span> <span class="variable">k</span> <span class="operator">=</span> i;</span><br><span class="line">    <span class="keyword">while</span> (i &lt;= iEnd &amp;&amp; j &lt;= jEnd) &#123;</span><br><span class="line">        <span class="keyword">if</span> (a1[i] &lt; a1[j]) &#123;</span><br><span class="line">            a2[k] = a1[i];</span><br><span class="line">            i++;</span><br><span class="line">        &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">            a2[k] = a1[j];</span><br><span class="line">            j++;</span><br><span class="line">        &#125;</span><br><span class="line">        k++;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">if</span> (i &gt; leftEnd) &#123;</span><br><span class="line">        System.arraycopy(a1, j, a2, k, jEnd - j + <span class="number">1</span>);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">if</span> (j &gt; rightEnd) &#123;</span><br><span class="line">        System.arraycopy(a1, i, a2, k, iEnd - i + <span class="number">1</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>测试</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="type">int</span>[] a1 = &#123;<span class="number">1</span>, <span class="number">5</span>, <span class="number">6</span>, <span class="number">2</span>, <span class="number">4</span>, <span class="number">10</span>, <span class="number">11</span>&#125;;</span><br><span class="line"><span class="type">int</span>[] a2 = <span class="keyword">new</span> <span class="title class_">int</span>[a3.length];</span><br><span class="line">merge(a1, <span class="number">0</span>, <span class="number">2</span>, <span class="number">3</span>, <span class="number">6</span>, a2);</span><br></pre></td></tr></table></figure>



<p>归并排序代码备份</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">split</span><span class="params">(<span class="type">int</span>[] a1, <span class="type">int</span> i, <span class="type">int</span> j, <span class="type">int</span>[] a2)</span> &#123;</span><br><span class="line">    System.out.println(<span class="string">&quot;i=&quot;</span> + i + <span class="string">&quot; j=&quot;</span> + j + <span class="string">&quot; a1=&quot;</span> + Arrays.toString(Arrays.copyOfRange(a1, i, j + <span class="number">1</span>)));</span><br><span class="line">    <span class="keyword">if</span> (i == j) &#123;</span><br><span class="line">        <span class="keyword">return</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="type">int</span> <span class="variable">m</span> <span class="operator">=</span> (i + j) &gt;&gt;&gt; <span class="number">1</span>;</span><br><span class="line">    split(a1, i, m, a2);</span><br><span class="line">    split(a1, m + <span class="number">1</span>, j, a2);</span><br><span class="line">    <span class="comment">//merge(a1, i, m, m+1, j, a2); // 非递归</span></span><br><span class="line">    <span class="comment">//merge(a1, i, m, m + 1, j, a2, i); // 递归</span></span><br><span class="line">    System.arraycopy(a2, i, a1, i, (j - i + <span class="number">1</span>));</span><br><span class="line">    System.out.println(<span class="string">&quot;i=&quot;</span> + i + <span class="string">&quot; m=&quot;</span> + m + <span class="string">&quot; j=&quot;</span> + j + <span class="string">&quot; a1=&quot;</span> + Arrays.toString(a1) + <span class="string">&quot; a2=&quot;</span> + Arrays.toString(a2));</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="type">int</span>[] a1 = &#123;<span class="number">1</span>, <span class="number">5</span>, <span class="number">6</span>, <span class="number">2</span>, <span class="number">4</span>, <span class="number">10</span>, <span class="number">11</span>&#125;;</span><br><span class="line"><span class="type">int</span>[] a2 = <span class="keyword">new</span> <span class="title class_">int</span>[a1.length];</span><br><span class="line">split(a1, <span class="number">0</span>, a1.length - <span class="number">1</span>, a2);</span><br><span class="line">System.out.println(Arrays.toString(a1));</span><br></pre></td></tr></table></figure>





<h2 id="3-7-队列"><a href="#3-7-队列" class="headerlink" title="3.7 队列"></a>3.7 队列</h2><h3 id="E01-二叉树层序遍历-力扣-102-题"><a href="#E01-二叉树层序遍历-力扣-102-题" class="headerlink" title="E01. 二叉树层序遍历-力扣 102 题"></a>E01. 二叉树层序遍历-力扣 102 题</h3><figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">Solution</span> &#123;</span><br><span class="line">    <span class="keyword">public</span> List&lt;List&lt;Integer&gt;&gt; <span class="title function_">levelOrder</span><span class="params">(TreeNode root)</span> &#123;</span><br><span class="line">        List&lt;List&lt;Integer&gt;&gt; result = <span class="keyword">new</span> <span class="title class_">ArrayList</span>&lt;&gt;();</span><br><span class="line">        <span class="keyword">if</span>(root == <span class="literal">null</span>) &#123;</span><br><span class="line">            <span class="keyword">return</span> result;</span><br><span class="line">        &#125;</span><br><span class="line">        LinkedListQueue&lt;TreeNode&gt; queue = <span class="keyword">new</span> <span class="title class_">LinkedListQueue</span>&lt;&gt;();</span><br><span class="line">        queue.offer(root);</span><br><span class="line">        <span class="type">int</span> <span class="variable">c1</span> <span class="operator">=</span> <span class="number">1</span>;		<span class="comment">// 本层节点个数</span></span><br><span class="line">        <span class="keyword">while</span> (!queue.isEmpty()) &#123;</span><br><span class="line">            <span class="type">int</span> <span class="variable">c2</span> <span class="operator">=</span> <span class="number">0</span>; 	<span class="comment">// 下层节点个数</span></span><br><span class="line">            List&lt;Integer&gt; level = <span class="keyword">new</span> <span class="title class_">ArrayList</span>&lt;&gt;();</span><br><span class="line">            <span class="keyword">for</span> (<span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> <span class="number">0</span>; i &lt; c1; i++) &#123;</span><br><span class="line">                <span class="type">TreeNode</span> <span class="variable">node</span> <span class="operator">=</span> queue.poll();</span><br><span class="line">                level.add(node.val);</span><br><span class="line">                <span class="keyword">if</span> (node.left != <span class="literal">null</span>) &#123;</span><br><span class="line">                    queue.offer(node.left);</span><br><span class="line">                    c2++;</span><br><span class="line">                &#125;</span><br><span class="line">                <span class="keyword">if</span> (node.right != <span class="literal">null</span>) &#123;</span><br><span class="line">                    queue.offer(node.right);</span><br><span class="line">                    c2++;</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line">            c1 = c2;</span><br><span class="line">            result.add(level);</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> result;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 自定义队列</span></span><br><span class="line">    <span class="keyword">static</span> <span class="keyword">class</span> <span class="title class_">LinkedListQueue</span>&lt;E&gt; &#123;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">class</span> <span class="title class_">Node</span>&lt;E&gt; &#123;</span><br><span class="line">            E value;</span><br><span class="line">            Node&lt;E&gt; next;</span><br><span class="line"></span><br><span class="line">            <span class="keyword">public</span> <span class="title function_">Node</span><span class="params">(E value, Node&lt;E&gt; next)</span> &#123;</span><br><span class="line">                <span class="built_in">this</span>.value = value;</span><br><span class="line">                <span class="built_in">this</span>.next = next;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">private</span> <span class="keyword">final</span> Node&lt;E&gt; head = <span class="keyword">new</span> <span class="title class_">Node</span>&lt;&gt;(<span class="literal">null</span>, <span class="literal">null</span>);</span><br><span class="line">        <span class="keyword">private</span> Node&lt;E&gt; tail = head;</span><br><span class="line">        <span class="type">int</span> <span class="variable">size</span> <span class="operator">=</span> <span class="number">0</span>;</span><br><span class="line">        <span class="keyword">private</span> <span class="type">int</span> <span class="variable">capacity</span> <span class="operator">=</span> Integer.MAX_VALUE;</span><br><span class="line"></span><br><span class="line">        &#123;</span><br><span class="line">            tail.next = head;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">public</span> <span class="title function_">LinkedListQueue</span><span class="params">()</span> &#123;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">public</span> <span class="title function_">LinkedListQueue</span><span class="params">(<span class="type">int</span> capacity)</span> &#123;</span><br><span class="line">            <span class="built_in">this</span>.capacity = capacity;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">public</span> <span class="type">boolean</span> <span class="title function_">offer</span><span class="params">(E value)</span> &#123;</span><br><span class="line">            <span class="keyword">if</span> (isFull()) &#123;</span><br><span class="line">                <span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line">            &#125;</span><br><span class="line">            Node&lt;E&gt; added = <span class="keyword">new</span> <span class="title class_">Node</span>&lt;&gt;(value, head);</span><br><span class="line">            tail.next = added;</span><br><span class="line">            tail = added;</span><br><span class="line">            size++;</span><br><span class="line">            <span class="keyword">return</span> <span class="literal">true</span>;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">public</span> E <span class="title function_">poll</span><span class="params">()</span> &#123;</span><br><span class="line">            <span class="keyword">if</span> (isEmpty()) &#123;</span><br><span class="line">                <span class="keyword">return</span> <span class="literal">null</span>;</span><br><span class="line">            &#125;</span><br><span class="line">            Node&lt;E&gt; first = head.next;</span><br><span class="line">            head.next = first.next;</span><br><span class="line">            <span class="keyword">if</span> (first == tail) &#123;</span><br><span class="line">                tail = head;</span><br><span class="line">            &#125;</span><br><span class="line">            size--;</span><br><span class="line">            <span class="keyword">return</span> first.value;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">public</span> E <span class="title function_">peek</span><span class="params">()</span> &#123;</span><br><span class="line">            <span class="keyword">if</span> (isEmpty()) &#123;</span><br><span class="line">                <span class="keyword">return</span> <span class="literal">null</span>;</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="keyword">return</span> head.next.value;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">public</span> <span class="type">boolean</span> <span class="title function_">isEmpty</span><span class="params">()</span> &#123;</span><br><span class="line">            <span class="keyword">return</span> head == tail;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">public</span> <span class="type">boolean</span> <span class="title function_">isFull</span><span class="params">()</span> &#123;</span><br><span class="line">            <span class="keyword">return</span> size == capacity;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>



<h3 id="Ex1-设计队列-力扣-622-题"><a href="#Ex1-设计队列-力扣-622-题" class="headerlink" title="Ex1. 设计队列-力扣 622 题"></a>Ex1. 设计队列-力扣 622 题</h3><p>由于与课堂例题差别不大，这里只给出参考解答</p>
<p>基于链表的实现</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Ex1Leetcode622</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">class</span> <span class="title class_">Node</span> &#123;</span><br><span class="line">        <span class="type">int</span> value;</span><br><span class="line">        Node next;</span><br><span class="line">        Node(<span class="type">int</span> value, Node next) &#123;</span><br><span class="line">            <span class="built_in">this</span>.value = value;</span><br><span class="line">            <span class="built_in">this</span>.next = next;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">final</span> <span class="type">Node</span> <span class="variable">head</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Node</span>(-<span class="number">1</span>, <span class="literal">null</span>);</span><br><span class="line">    <span class="keyword">private</span> <span class="type">Node</span> <span class="variable">tail</span> <span class="operator">=</span> head;</span><br><span class="line">    <span class="keyword">private</span> <span class="type">int</span> <span class="variable">size</span> <span class="operator">=</span> <span class="number">0</span>;</span><br><span class="line">    <span class="keyword">private</span> <span class="type">int</span> <span class="variable">capacity</span> <span class="operator">=</span> <span class="number">0</span>;</span><br><span class="line"></span><br><span class="line">    &#123;</span><br><span class="line">        tail.next = head;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="title function_">Ex1Leetcode622</span><span class="params">(<span class="type">int</span> capacity)</span> &#123;</span><br><span class="line">        <span class="built_in">this</span>.capacity = capacity;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="type">boolean</span> <span class="title function_">enQueue</span><span class="params">(<span class="type">int</span> value)</span> &#123;</span><br><span class="line">        <span class="keyword">if</span>(isFull()) &#123;</span><br><span class="line">            <span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="type">Node</span> <span class="variable">added</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Node</span>(value, head);</span><br><span class="line">        tail.next = added;</span><br><span class="line">        tail = added;</span><br><span class="line">        size++;</span><br><span class="line">        <span class="keyword">return</span> <span class="literal">true</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="type">boolean</span> <span class="title function_">deQueue</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">if</span>(isEmpty()) &#123;</span><br><span class="line">            <span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="type">Node</span> <span class="variable">first</span> <span class="operator">=</span> head.next;</span><br><span class="line">        head.next = first.next;</span><br><span class="line">        <span class="keyword">if</span> (first == tail) &#123;</span><br><span class="line">            tail = head;</span><br><span class="line">        &#125;</span><br><span class="line">        size--;</span><br><span class="line">        <span class="keyword">return</span> <span class="literal">true</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="type">int</span> <span class="title function_">Front</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">if</span>(isEmpty()) &#123;</span><br><span class="line">            <span class="keyword">return</span> -<span class="number">1</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> head.next.value;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="type">int</span> <span class="title function_">Rear</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">if</span>(isEmpty()) &#123;</span><br><span class="line">            <span class="keyword">return</span> -<span class="number">1</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> tail.value;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="type">boolean</span> <span class="title function_">isEmpty</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> head == tail;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="type">boolean</span> <span class="title function_">isFull</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> size == capacity;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>注意：</p>
<ul>
<li>Leetcode 的实现里 deQueue（出队）返回值是布尔值，并不会返回队头元素</li>
<li>它期望用法是先用 Front 返回对头元素，再 deQueue 出队</li>
</ul>
<h2 id="3-8-栈"><a href="#3-8-栈" class="headerlink" title="3.8 栈"></a>3.8 栈</h2><h3 id="E01-有效的括号-力扣-20-题"><a href="#E01-有效的括号-力扣-20-题" class="headerlink" title="E01. 有效的括号-力扣 20 题"></a>E01. 有效的括号-力扣 20 题</h3><p>一个字符串中可能出现 <code>[]</code> <code>()</code> 和 <code>&#123;&#125;</code> 三种括号，判断该括号是否有效</p>
<p>有效的例子</p>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">()[]&#123;&#125;</span><br><span class="line"></span><br><span class="line">([&#123;&#125;])</span><br><span class="line"></span><br><span class="line">()</span><br></pre></td></tr></table></figure>

<p>无效的例子</p>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">[)</span><br><span class="line"></span><br><span class="line">([)]</span><br><span class="line"></span><br><span class="line">([]</span><br></pre></td></tr></table></figure>

<p>思路</p>
<ul>
<li>遇到左括号, 把要配对的右括号放入栈顶</li>
<li>遇到右括号, 若此时栈为空, 返回 false，否则把它与栈顶元素对比<ul>
<li>若相等, 栈顶元素弹出, 继续对比下一组</li>
<li>若不等, 无效括号直接返回 false</li>
</ul>
</li>
<li>循环结束<ul>
<li>若栈为空, 表示所有括号都配上对, 返回 true</li>
<li>若栈不为空, 表示右没配对的括号, 应返回 false</li>
</ul>
</li>
</ul>
<p>答案（用到了课堂案例中的 ArrayStack 类）</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="type">boolean</span> <span class="title function_">isValid</span><span class="params">(String s)</span> &#123;</span><br><span class="line">    ArrayStack&lt;Character&gt; stack = <span class="keyword">new</span> <span class="title class_">ArrayStack</span>&lt;&gt;(s.length() / <span class="number">2</span> + <span class="number">1</span>);</span><br><span class="line">    <span class="keyword">for</span> (<span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> <span class="number">0</span>; i &lt; s.length(); i++) &#123;</span><br><span class="line">        <span class="type">char</span> <span class="variable">c</span> <span class="operator">=</span> s.charAt(i);</span><br><span class="line">        <span class="keyword">if</span> (c == <span class="string">&#x27;(&#x27;</span>) &#123;</span><br><span class="line">            stack.push(<span class="string">&#x27;)&#x27;</span>);</span><br><span class="line">        &#125; <span class="keyword">else</span> <span class="keyword">if</span> (c == <span class="string">&#x27;[&#x27;</span>) &#123;</span><br><span class="line">            stack.push(<span class="string">&#x27;]&#x27;</span>);</span><br><span class="line">        &#125; <span class="keyword">else</span> <span class="keyword">if</span> (c == <span class="string">&#x27;&#123;&#x27;</span>) &#123;</span><br><span class="line">            stack.push(<span class="string">&#x27;&#125;&#x27;</span>);</span><br><span class="line">        &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">            <span class="keyword">if</span> (!stack.isEmpty() &amp;&amp; stack.peek() == c) &#123;</span><br><span class="line">                stack.pop();</span><br><span class="line">            &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">                <span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> stack.isEmpty();</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>



<h3 id="E02-后缀表达式求值-力扣-120-题"><a href="#E02-后缀表达式求值-力扣-120-题" class="headerlink" title="E02. 后缀表达式求值-力扣 120 题"></a>E02. 后缀表达式求值-力扣 120 题</h3><p>后缀表达式也称为逆波兰表达式，即运算符写在后面</p>
<ul>
<li>从左向右进行计算</li>
<li>不必考虑运算符优先级，即不用包含括号</li>
</ul>
<p>示例</p>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">输入：tokens = [&quot;2&quot;,&quot;1&quot;,&quot;+&quot;,&quot;3&quot;,&quot;*&quot;]</span><br><span class="line">输出：9</span><br><span class="line">即：(2 + 1) * 3</span><br><span class="line"></span><br><span class="line">输入：tokens = [&quot;4&quot;,&quot;13&quot;,&quot;5&quot;,&quot;/&quot;,&quot;+&quot;]</span><br><span class="line">输出：6</span><br><span class="line">即：4 + (13 / 5)</span><br></pre></td></tr></table></figure>

<p>题目假设</p>
<ul>
<li>数字都视为整数</li>
<li>数字和运算符个数给定正确，不会有除零发生</li>
</ul>
<p>代码</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="type">int</span> <span class="title function_">evalRPN</span><span class="params">(String[] tokens)</span> &#123;</span><br><span class="line">    LinkedList&lt;Integer&gt; numbers = <span class="keyword">new</span> <span class="title class_">LinkedList</span>&lt;&gt;();</span><br><span class="line">    <span class="keyword">for</span> (String t : tokens) &#123;</span><br><span class="line">        <span class="keyword">switch</span> (t) &#123;</span><br><span class="line">            <span class="keyword">case</span> <span class="string">&quot;+&quot;</span> -&gt; &#123;</span><br><span class="line">                <span class="type">Integer</span> <span class="variable">b</span> <span class="operator">=</span> numbers.pop();</span><br><span class="line">                <span class="type">Integer</span> <span class="variable">a</span> <span class="operator">=</span> numbers.pop();</span><br><span class="line">                numbers.push(a + b);</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="keyword">case</span> <span class="string">&quot;-&quot;</span> -&gt; &#123;</span><br><span class="line">                <span class="type">Integer</span> <span class="variable">b</span> <span class="operator">=</span> numbers.pop();</span><br><span class="line">                <span class="type">Integer</span> <span class="variable">a</span> <span class="operator">=</span> numbers.pop();</span><br><span class="line">                numbers.push(a - b);</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="keyword">case</span> <span class="string">&quot;*&quot;</span> -&gt; &#123;</span><br><span class="line">                <span class="type">Integer</span> <span class="variable">b</span> <span class="operator">=</span> numbers.pop();</span><br><span class="line">                <span class="type">Integer</span> <span class="variable">a</span> <span class="operator">=</span> numbers.pop();</span><br><span class="line">                numbers.push(a * b);</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="keyword">case</span> <span class="string">&quot;/&quot;</span> -&gt; &#123;</span><br><span class="line">                <span class="type">Integer</span> <span class="variable">b</span> <span class="operator">=</span> numbers.pop();</span><br><span class="line">                <span class="type">Integer</span> <span class="variable">a</span> <span class="operator">=</span> numbers.pop();</span><br><span class="line">                numbers.push(a / b);</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="keyword">default</span> -&gt; numbers.push(Integer.parseInt(t));</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> numbers.pop();</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>



<h3 id="E03-中缀表达式转后缀"><a href="#E03-中缀表达式转后缀" class="headerlink" title="E03. 中缀表达式转后缀"></a>E03. 中缀表达式转后缀</h3><figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">E03InfixToSuffix</span> &#123;</span><br><span class="line">    <span class="comment">/*</span></span><br><span class="line"><span class="comment">        思路</span></span><br><span class="line"><span class="comment">        1. 遇到数字, 拼串</span></span><br><span class="line"><span class="comment">        2. 遇到 + - * /</span></span><br><span class="line"><span class="comment">            - 优先级高于栈顶运算符 入栈</span></span><br><span class="line"><span class="comment">            - 否则将栈中高级或平级运算符出栈拼串, 本运算符入栈</span></span><br><span class="line"><span class="comment">        3. 遍历完成, 栈中剩余运算符出栈拼串</span></span><br><span class="line"><span class="comment">            - 先出栈,意味着优先运算</span></span><br><span class="line"><span class="comment">        4. 带 ()</span></span><br><span class="line"><span class="comment">            - 左括号直接入栈</span></span><br><span class="line"><span class="comment">            - 右括号要将栈中直至左括号为止的运算符出栈拼串</span></span><br><span class="line"><span class="comment"></span></span><br><span class="line"><span class="comment">        |   |</span></span><br><span class="line"><span class="comment">        |   |</span></span><br><span class="line"><span class="comment">        |   |</span></span><br><span class="line"><span class="comment">        _____</span></span><br><span class="line"><span class="comment"></span></span><br><span class="line"><span class="comment">        a+b</span></span><br><span class="line"><span class="comment">        a+b-c</span></span><br><span class="line"><span class="comment">        a+b*c</span></span><br><span class="line"><span class="comment">        a*b+c</span></span><br><span class="line"><span class="comment">        (a+b)*c</span></span><br><span class="line"><span class="comment"></span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> &#123;</span><br><span class="line">        System.out.println(infixToSuffix(<span class="string">&quot;a+b&quot;</span>));</span><br><span class="line">        System.out.println(infixToSuffix(<span class="string">&quot;a+b-c&quot;</span>));</span><br><span class="line">        System.out.println(infixToSuffix(<span class="string">&quot;a+b*c&quot;</span>));</span><br><span class="line">        System.out.println(infixToSuffix(<span class="string">&quot;a*b-c&quot;</span>));</span><br><span class="line">        System.out.println(infixToSuffix(<span class="string">&quot;(a+b)*c&quot;</span>));</span><br><span class="line">        System.out.println(infixToSuffix(<span class="string">&quot;a+b*c+(d*e+f)*g&quot;</span>));</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">static</span> String <span class="title function_">infixToSuffix</span><span class="params">(String exp)</span> &#123;</span><br><span class="line">        LinkedList&lt;Character&gt; stack = <span class="keyword">new</span> <span class="title class_">LinkedList</span>&lt;&gt;();</span><br><span class="line">        <span class="type">StringBuilder</span> <span class="variable">sb</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">StringBuilder</span>(exp.length());</span><br><span class="line">        <span class="keyword">for</span> (<span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> <span class="number">0</span>; i &lt; exp.length(); i++) &#123;</span><br><span class="line">            <span class="type">char</span> <span class="variable">c</span> <span class="operator">=</span> exp.charAt(i);</span><br><span class="line">            <span class="keyword">switch</span> (c) &#123;</span><br><span class="line">                <span class="keyword">case</span> <span class="string">&#x27;+&#x27;</span>, <span class="string">&#x27;-&#x27;</span>, <span class="string">&#x27;*&#x27;</span>, <span class="string">&#x27;/&#x27;</span> -&gt; &#123;</span><br><span class="line">                    <span class="keyword">if</span> (stack.isEmpty()) &#123;</span><br><span class="line">                        stack.push(c);</span><br><span class="line">                    &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">                        <span class="keyword">if</span> (priority(c) &gt; priority(stack.peek())) &#123;</span><br><span class="line">                            stack.push(c);</span><br><span class="line">                        &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">                            <span class="keyword">while</span> (!stack.isEmpty() </span><br><span class="line">                                   &amp;&amp; priority(stack.peek()) &gt;= priority(c)) &#123;</span><br><span class="line">                                sb.append(stack.pop());</span><br><span class="line">                            &#125;</span><br><span class="line">                            stack.push(c);</span><br><span class="line">                        &#125;</span><br><span class="line">                    &#125;</span><br><span class="line">                &#125;</span><br><span class="line">                <span class="keyword">case</span> <span class="string">&#x27;(&#x27;</span> -&gt; &#123;</span><br><span class="line">                    stack.push(c);</span><br><span class="line">                &#125;</span><br><span class="line">                <span class="keyword">case</span> <span class="string">&#x27;)&#x27;</span> -&gt; &#123;</span><br><span class="line">                    <span class="keyword">while</span> (!stack.isEmpty() &amp;&amp; stack.peek() != <span class="string">&#x27;(&#x27;</span>) &#123;</span><br><span class="line">                        sb.append(stack.pop());</span><br><span class="line">                    &#125;</span><br><span class="line">                    stack.pop();</span><br><span class="line">                &#125;</span><br><span class="line">                <span class="keyword">default</span> -&gt; &#123;</span><br><span class="line">                    sb.append(c);</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">while</span> (!stack.isEmpty()) &#123;</span><br><span class="line">            sb.append(stack.pop());</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> sb.toString();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">static</span> <span class="type">int</span> <span class="title function_">priority</span><span class="params">(<span class="type">char</span> c)</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">switch</span> (c) &#123;</span><br><span class="line">            <span class="keyword">case</span> <span class="string">&#x27;(&#x27;</span> -&gt; <span class="number">0</span>;</span><br><span class="line">            <span class="keyword">case</span> <span class="string">&#x27;*&#x27;</span>, <span class="string">&#x27;/&#x27;</span> -&gt; <span class="number">2</span>;</span><br><span class="line">            <span class="keyword">case</span> <span class="string">&#x27;+&#x27;</span>, <span class="string">&#x27;-&#x27;</span> -&gt; <span class="number">1</span>;</span><br><span class="line">            <span class="keyword">default</span> -&gt; <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">IllegalArgumentException</span>(<span class="string">&quot;不合法字符:&quot;</span> + c);</span><br><span class="line">        &#125;;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>



<h3 id="E04-双栈模拟队列-力扣-232-题"><a href="#E04-双栈模拟队列-力扣-232-题" class="headerlink" title="E04. 双栈模拟队列-力扣 232 题"></a>E04. 双栈模拟队列-力扣 232 题</h3><p>给力扣题目用的<strong>自实现</strong>栈，可以定义为静态内部类</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">ArrayStack</span>&lt;E&gt; &#123;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> E[] array;</span><br><span class="line">    <span class="keyword">private</span> <span class="type">int</span> top; <span class="comment">// 栈顶指针</span></span><br><span class="line"></span><br><span class="line">    <span class="meta">@SuppressWarnings(&quot;all&quot;)</span></span><br><span class="line">    <span class="keyword">public</span> <span class="title function_">ArrayStack</span><span class="params">(<span class="type">int</span> capacity)</span> &#123;</span><br><span class="line">        <span class="built_in">this</span>.array = (E[]) <span class="keyword">new</span> <span class="title class_">Object</span>[capacity];</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="type">boolean</span> <span class="title function_">push</span><span class="params">(E value)</span> &#123;</span><br><span class="line">        <span class="keyword">if</span> (isFull()) &#123;</span><br><span class="line">            <span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        array[top++] = value;</span><br><span class="line">        <span class="keyword">return</span> <span class="literal">true</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> E <span class="title function_">pop</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">if</span> (isEmpty()) &#123;</span><br><span class="line">            <span class="keyword">return</span> <span class="literal">null</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> array[--top];</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> E <span class="title function_">peek</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">if</span> (isEmpty()) &#123;</span><br><span class="line">            <span class="keyword">return</span> <span class="literal">null</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> array[top - <span class="number">1</span>];</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="type">boolean</span> <span class="title function_">isEmpty</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> top == <span class="number">0</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="type">boolean</span> <span class="title function_">isFull</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> top == array.length;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>参考解答，注意：题目已说明</p>
<ul>
<li>调用 push、pop 等方法的次数最多 100</li>
</ul>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">E04Leetcode232</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/*</span></span><br><span class="line"><span class="comment">        队列头      队列尾</span></span><br><span class="line"><span class="comment">        s1       s2</span></span><br><span class="line"><span class="comment">        顶   底   底   顶</span></span><br><span class="line"><span class="comment">                 abc</span></span><br><span class="line"><span class="comment"></span></span><br><span class="line"><span class="comment">        push(a)</span></span><br><span class="line"><span class="comment">        push(b)</span></span><br><span class="line"><span class="comment">        push(c)</span></span><br><span class="line"><span class="comment">        pop()</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    ArrayStack&lt;Integer&gt; s1 = <span class="keyword">new</span> <span class="title class_">ArrayStack</span>&lt;&gt;(<span class="number">100</span>);</span><br><span class="line">    ArrayStack&lt;Integer&gt; s2 = <span class="keyword">new</span> <span class="title class_">ArrayStack</span>&lt;&gt;(<span class="number">100</span>);</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">push</span><span class="params">(<span class="type">int</span> x)</span> &#123;</span><br><span class="line">        s2.push(x);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="type">int</span> <span class="title function_">pop</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">if</span> (s1.isEmpty()) &#123;</span><br><span class="line">            <span class="keyword">while</span> (!s2.isEmpty()) &#123;</span><br><span class="line">                s1.push(s2.pop());</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> s1.pop();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="type">int</span> <span class="title function_">peek</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">if</span> (s1.isEmpty()) &#123;</span><br><span class="line">            <span class="keyword">while</span> (!s2.isEmpty()) &#123;</span><br><span class="line">                s1.push(s2.pop());</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> s1.peek();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="type">boolean</span> <span class="title function_">empty</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> s1.isEmpty() &amp;&amp; s2.isEmpty();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>



<h3 id="E05-单队列模拟栈-力扣-225-题"><a href="#E05-单队列模拟栈-力扣-225-题" class="headerlink" title="E05. 单队列模拟栈-力扣 225 题"></a>E05. 单队列模拟栈-力扣 225 题</h3><p>给力扣题目用的<strong>自实现</strong>队列，可以定义为静态内部类</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">ArrayQueue3</span>&lt;E&gt; &#123;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">final</span> E[] array;</span><br><span class="line">    <span class="type">int</span> <span class="variable">head</span> <span class="operator">=</span> <span class="number">0</span>;</span><br><span class="line">    <span class="type">int</span> <span class="variable">tail</span> <span class="operator">=</span> <span class="number">0</span>;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@SuppressWarnings(&quot;all&quot;)</span></span><br><span class="line">    <span class="keyword">public</span> <span class="title function_">ArrayQueue3</span><span class="params">(<span class="type">int</span> c)</span> &#123;</span><br><span class="line">        c -= <span class="number">1</span>;</span><br><span class="line">        c |= c &gt;&gt; <span class="number">1</span>;</span><br><span class="line">        c |= c &gt;&gt; <span class="number">2</span>;</span><br><span class="line">        c |= c &gt;&gt; <span class="number">4</span>;</span><br><span class="line">        c |= c &gt;&gt; <span class="number">8</span>;</span><br><span class="line">        c |= c &gt;&gt; <span class="number">16</span>;</span><br><span class="line">        c += <span class="number">1</span>;</span><br><span class="line">        array = (E[]) <span class="keyword">new</span> <span class="title class_">Object</span>[c];</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">public</span> <span class="type">boolean</span> <span class="title function_">offer</span><span class="params">(E value)</span> &#123;</span><br><span class="line">        <span class="keyword">if</span> (isFull()) &#123;</span><br><span class="line">            <span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line">        &#125;        </span><br><span class="line">        array[tail &amp; (array.length - <span class="number">1</span>)] = value;</span><br><span class="line">        tail++;</span><br><span class="line">        <span class="keyword">return</span> <span class="literal">true</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> E <span class="title function_">poll</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">if</span> (isEmpty()) &#123;</span><br><span class="line">            <span class="keyword">return</span> <span class="literal">null</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="type">E</span> <span class="variable">value</span> <span class="operator">=</span> array[head &amp; (array.length - <span class="number">1</span>)];</span><br><span class="line">        head++;</span><br><span class="line">        <span class="keyword">return</span> value;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> E <span class="title function_">peek</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">if</span> (isEmpty()) &#123;</span><br><span class="line">            <span class="keyword">return</span> <span class="literal">null</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> array[head &amp; (array.length - <span class="number">1</span>)];</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="type">boolean</span> <span class="title function_">isEmpty</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> head == tail;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="type">boolean</span> <span class="title function_">isFull</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> tail - head == array.length;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>参考解答，注意：题目已说明</p>
<ul>
<li>调用 push、pop 等方法的次数最多 100</li>
<li>每次调用 pop 和 top 都能保证栈不为空</li>
</ul>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">E05Leetcode225</span> &#123;</span><br><span class="line">    <span class="comment">/*</span></span><br><span class="line"><span class="comment">        队列头     队列尾</span></span><br><span class="line"><span class="comment">        cba</span></span><br><span class="line"><span class="comment">        顶           底</span></span><br><span class="line"><span class="comment"></span></span><br><span class="line"><span class="comment">        queue.offer(a)</span></span><br><span class="line"><span class="comment">        queue.offer(b)</span></span><br><span class="line"><span class="comment">        queue.offer(c)</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    ArrayQueue3&lt;Integer&gt; queue = <span class="keyword">new</span> <span class="title class_">ArrayQueue3</span>&lt;&gt;(<span class="number">100</span>);</span><br><span class="line">    <span class="type">int</span> <span class="variable">size</span> <span class="operator">=</span> <span class="number">0</span>;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">push</span><span class="params">(<span class="type">int</span> x)</span> &#123;</span><br><span class="line">        queue.offer(x);</span><br><span class="line">        <span class="keyword">for</span> (<span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> <span class="number">0</span>; i &lt; size; i++) &#123;</span><br><span class="line">            queue.offer(queue.poll());</span><br><span class="line">        &#125;</span><br><span class="line">        size++;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="type">int</span> <span class="title function_">pop</span><span class="params">()</span> &#123;</span><br><span class="line">        size--;</span><br><span class="line">        <span class="keyword">return</span> queue.poll();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="type">int</span> <span class="title function_">top</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> queue.peek();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="type">boolean</span> <span class="title function_">empty</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> queue.isEmpty();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>



<h2 id="3-9-双端队列"><a href="#3-9-双端队列" class="headerlink" title="3.9 双端队列"></a>3.9 双端队列</h2><h3 id="E01-二叉树-Z-字层序遍历-力扣-103-题"><a href="#E01-二叉树-Z-字层序遍历-力扣-103-题" class="headerlink" title="E01. 二叉树 Z 字层序遍历-力扣 103 题"></a>E01. 二叉树 Z 字层序遍历-力扣 103 题</h3><figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">E01Leetcode103</span> &#123;</span><br><span class="line">    <span class="keyword">public</span> List&lt;List&lt;Integer&gt;&gt; <span class="title function_">zigzagLevelOrder</span><span class="params">(TreeNode root)</span> &#123;</span><br><span class="line">        List&lt;List&lt;Integer&gt;&gt; result = <span class="keyword">new</span> <span class="title class_">ArrayList</span>&lt;&gt;();</span><br><span class="line">        <span class="keyword">if</span> (root == <span class="literal">null</span>) &#123;</span><br><span class="line">            <span class="keyword">return</span> result;</span><br><span class="line">        &#125;</span><br><span class="line">        LinkedList&lt;TreeNode&gt; queue = <span class="keyword">new</span> <span class="title class_">LinkedList</span>&lt;&gt;();</span><br><span class="line">        queue.offer(root);</span><br><span class="line">        <span class="type">boolean</span> <span class="variable">leftToRight</span> <span class="operator">=</span> <span class="literal">true</span>;</span><br><span class="line">        <span class="type">int</span> <span class="variable">c1</span> <span class="operator">=</span> <span class="number">1</span>;</span><br><span class="line">        <span class="keyword">while</span> (!queue.isEmpty()) &#123;</span><br><span class="line">            <span class="type">int</span> <span class="variable">c2</span> <span class="operator">=</span> <span class="number">0</span>;</span><br><span class="line">            LinkedList&lt;Integer&gt; deque = <span class="keyword">new</span> <span class="title class_">LinkedList</span>&lt;&gt;();</span><br><span class="line">            <span class="keyword">for</span> (<span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> <span class="number">0</span>; i &lt; c1; i++) &#123;</span><br><span class="line">                <span class="type">TreeNode</span> <span class="variable">n</span> <span class="operator">=</span> queue.poll();</span><br><span class="line">                <span class="keyword">if</span> (leftToRight) &#123;</span><br><span class="line">                    deque.offerLast(n.val);</span><br><span class="line">                &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">                    deque.offerFirst(n.val);</span><br><span class="line">                &#125;</span><br><span class="line">                <span class="keyword">if</span> (n.left != <span class="literal">null</span>) &#123;</span><br><span class="line">                    queue.offer(n.left);</span><br><span class="line">                    c2++;</span><br><span class="line">                &#125;</span><br><span class="line">                <span class="keyword">if</span> (n.right != <span class="literal">null</span>) &#123;</span><br><span class="line">                    queue.offer(n.right);</span><br><span class="line">                    c2++;</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line">            c1 = c2;</span><br><span class="line">            leftToRight = !leftToRight;</span><br><span class="line">            result.add(deque);</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">return</span> result;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> &#123;</span><br><span class="line">        <span class="type">TreeNode</span> <span class="variable">root</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">TreeNode</span>(</span><br><span class="line">                <span class="keyword">new</span> <span class="title class_">TreeNode</span>(</span><br><span class="line">                        <span class="keyword">new</span> <span class="title class_">TreeNode</span>(<span class="number">4</span>),</span><br><span class="line">                        <span class="number">2</span>,</span><br><span class="line">                        <span class="keyword">new</span> <span class="title class_">TreeNode</span>(<span class="number">5</span>)</span><br><span class="line">                ),</span><br><span class="line">                <span class="number">1</span>,</span><br><span class="line">                <span class="keyword">new</span> <span class="title class_">TreeNode</span>(</span><br><span class="line">                        <span class="keyword">new</span> <span class="title class_">TreeNode</span>(<span class="number">6</span>),</span><br><span class="line">                        <span class="number">3</span>,</span><br><span class="line">                        <span class="keyword">new</span> <span class="title class_">TreeNode</span>(<span class="number">7</span>)</span><br><span class="line">                )</span><br><span class="line">        );</span><br><span class="line">        List&lt;List&lt;Integer&gt;&gt; lists = <span class="keyword">new</span> <span class="title class_">E01Leetcode103</span>().zigzagLevelOrder(root);</span><br><span class="line">        <span class="keyword">for</span> (List&lt;Integer&gt; list : lists) &#123;</span><br><span class="line">            System.out.println(list);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>



<h3 id="Ex1-设计双端队列-力扣-641-题"><a href="#Ex1-设计双端队列-力扣-641-题" class="headerlink" title="Ex1. 设计双端队列-力扣 641 题"></a>Ex1. 设计双端队列-力扣 641 题</h3><p>与课堂例题也是差别不大，请参考</p>
<h2 id="3-10-优先级队列"><a href="#3-10-优先级队列" class="headerlink" title="3.10 优先级队列"></a>3.10 优先级队列</h2><h3 id="E01-合并多个有序链表-力扣-23-题"><a href="#E01-合并多个有序链表-力扣-23-题" class="headerlink" title="E01. 合并多个有序链表-力扣 23 题"></a>E01. 合并多个有序链表-力扣 23 题</h3><p>这道题目之前解答过，现在用刚学的优先级队列来实现一下</p>
<p>题目中要从小到大排列，因此选择用小顶堆来实现，自定义小顶堆如下</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">MinHeap</span> &#123;</span><br><span class="line"></span><br><span class="line">    ListNode[] array;</span><br><span class="line">    <span class="type">int</span> size;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="title function_">MinHeap</span><span class="params">(<span class="type">int</span> capacity)</span> &#123;</span><br><span class="line">        array = <span class="keyword">new</span> <span class="title class_">ListNode</span>[capacity];</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">offer</span><span class="params">(ListNode offered)</span> &#123;</span><br><span class="line">        <span class="type">int</span> <span class="variable">child</span> <span class="operator">=</span> size++;</span><br><span class="line">        <span class="type">int</span> <span class="variable">parent</span> <span class="operator">=</span> (child - <span class="number">1</span>) / <span class="number">2</span>;</span><br><span class="line">        <span class="keyword">while</span> (child &gt; <span class="number">0</span> &amp;&amp; offered.val &lt; array[parent].val) &#123;</span><br><span class="line">            array[child] = array[parent];</span><br><span class="line">            child = parent;</span><br><span class="line">            parent = (child - <span class="number">1</span>) / <span class="number">2</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        array[child] = offered;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> ListNode <span class="title function_">poll</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">if</span> (isEmpty()) &#123;</span><br><span class="line">            <span class="keyword">return</span> <span class="literal">null</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        swap(<span class="number">0</span>, size - <span class="number">1</span>);</span><br><span class="line">        size--;</span><br><span class="line">        <span class="type">ListNode</span> <span class="variable">e</span> <span class="operator">=</span> array[size];</span><br><span class="line">        array[size] = <span class="literal">null</span>; <span class="comment">// help GC</span></span><br><span class="line"></span><br><span class="line">        down(<span class="number">0</span>);</span><br><span class="line"></span><br><span class="line">        <span class="keyword">return</span> e;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">void</span> <span class="title function_">down</span><span class="params">(<span class="type">int</span> parent)</span> &#123;</span><br><span class="line">        <span class="type">int</span> <span class="variable">left</span> <span class="operator">=</span> <span class="number">2</span> * parent + <span class="number">1</span>;</span><br><span class="line">        <span class="type">int</span> <span class="variable">right</span> <span class="operator">=</span> left + <span class="number">1</span>;</span><br><span class="line">        <span class="type">int</span> <span class="variable">min</span> <span class="operator">=</span> parent;</span><br><span class="line">        <span class="keyword">if</span> (left &lt; size &amp;&amp; array[left].val &lt; array[min].val) &#123;</span><br><span class="line">            min = left;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">if</span> (right &lt; size &amp;&amp; array[right].val &lt; array[min].val) &#123;</span><br><span class="line">            min = right;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">if</span> (min != parent) &#123;</span><br><span class="line">            swap(min, parent);</span><br><span class="line">            down(min);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">void</span> <span class="title function_">swap</span><span class="params">(<span class="type">int</span> i, <span class="type">int</span> j)</span> &#123;</span><br><span class="line">        <span class="type">ListNode</span> <span class="variable">t</span> <span class="operator">=</span> array[i];</span><br><span class="line">        array[i] = array[j];</span><br><span class="line">        array[j] = t;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="type">boolean</span> <span class="title function_">isEmpty</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> size == <span class="number">0</span>;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>代码</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">E01Leetcode23</span> &#123;</span><br><span class="line">    <span class="keyword">public</span> ListNode <span class="title function_">mergeKLists</span><span class="params">(ListNode[] lists)</span> &#123;</span><br><span class="line">        <span class="comment">// 1. 使用 jdk 的优先级队列实现</span></span><br><span class="line"><span class="comment">//        PriorityQueue&lt;ListNode&gt; queue = new PriorityQueue&lt;&gt;(Comparator.comparingInt(a -&gt; a.val));</span></span><br><span class="line">        <span class="comment">// 2. 使用自定义小顶堆实现</span></span><br><span class="line">        <span class="type">MinHeap</span> <span class="variable">queue</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">MinHeap</span>(lists.length);</span><br><span class="line">        <span class="keyword">for</span> (ListNode head : lists) &#123;</span><br><span class="line">            <span class="keyword">if</span> (head != <span class="literal">null</span>) &#123;</span><br><span class="line">                queue.offer(head);</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="type">ListNode</span> <span class="variable">s</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">ListNode</span>(-<span class="number">1</span>, <span class="literal">null</span>);</span><br><span class="line">        <span class="type">ListNode</span> <span class="variable">p</span> <span class="operator">=</span> s;</span><br><span class="line">        <span class="keyword">while</span> (!queue.isEmpty()) &#123;</span><br><span class="line">            <span class="type">ListNode</span> <span class="variable">node</span> <span class="operator">=</span> queue.poll();</span><br><span class="line">            p.next = node;</span><br><span class="line">            p = node;</span><br><span class="line">            <span class="keyword">if</span> (node.next != <span class="literal">null</span>) &#123;</span><br><span class="line">                queue.offer(node.next);</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> s.next;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>提问：</p>
<ul>
<li>能否将每个链表的所有元素全部加入堆，再一个个从堆顶移除？</li>
</ul>
<p>回答：</p>
<ul>
<li>可以是可以，但对空间占用就高了，堆的一个优点就是用有限的空间做事情</li>
</ul>
<h2 id="3-11-堆"><a href="#3-11-堆" class="headerlink" title="3.11 堆"></a>3.11 堆</h2><h3 id="E01-堆排序"><a href="#E01-堆排序" class="headerlink" title="E01. 堆排序"></a>E01. 堆排序</h3><p>算法描述</p>
<ol>
<li>heapify 建立大顶堆</li>
<li>将堆顶与堆底交换（最大元素被交换到堆底），缩小并下潜调整堆</li>
<li>重复第二步直至堆里剩一个元素</li>
</ol>
<p>可以使用之前课堂例题的大顶堆来实现</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="type">int</span>[] array = &#123;<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>, <span class="number">4</span>, <span class="number">5</span>, <span class="number">6</span>, <span class="number">7</span>&#125;;</span><br><span class="line"><span class="type">MaxHeap</span> <span class="variable">maxHeap</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">MaxHeap</span>(array);</span><br><span class="line">System.out.println(Arrays.toString(maxHeap.array));</span><br><span class="line"></span><br><span class="line"><span class="keyword">while</span> (maxHeap.size &gt; <span class="number">1</span>) &#123;</span><br><span class="line">    maxHeap.swap(<span class="number">0</span>, maxHeap.size - <span class="number">1</span>);</span><br><span class="line">    maxHeap.size--;</span><br><span class="line">    maxHeap.down(<span class="number">0</span>);</span><br><span class="line">&#125;</span><br><span class="line">System.out.println(Arrays.toString(maxHeap.array));</span><br></pre></td></tr></table></figure>



<h3 id="E02-数组中第K大元素-力扣-215-题"><a href="#E02-数组中第K大元素-力扣-215-题" class="headerlink" title="E02. 数组中第K大元素-力扣 215 题"></a>E02. 数组中第K大元素-力扣 215 题</h3><p>小顶堆（可删去用不到代码）</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">MinHeap</span> &#123;</span><br><span class="line">    <span class="type">int</span>[] array;</span><br><span class="line">    <span class="type">int</span> size;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="title function_">MinHeap</span><span class="params">(<span class="type">int</span> capacity)</span> &#123;</span><br><span class="line">        array = <span class="keyword">new</span> <span class="title class_">int</span>[capacity];</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">void</span> <span class="title function_">heapify</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">for</span> (<span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> (size &gt;&gt; <span class="number">1</span>) - <span class="number">1</span>; i &gt;= <span class="number">0</span>; i--) &#123;</span><br><span class="line">            down(i);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="type">int</span> <span class="title function_">poll</span><span class="params">()</span> &#123;</span><br><span class="line">        swap(<span class="number">0</span>, size - <span class="number">1</span>);</span><br><span class="line">        size--;</span><br><span class="line">        down(<span class="number">0</span>);</span><br><span class="line">        <span class="keyword">return</span> array[size];</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="type">int</span> <span class="title function_">poll</span><span class="params">(<span class="type">int</span> index)</span> &#123;</span><br><span class="line">        swap(index, size - <span class="number">1</span>);</span><br><span class="line">        size--;</span><br><span class="line">        down(index);</span><br><span class="line">        <span class="keyword">return</span> array[size];</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="type">int</span> <span class="title function_">peek</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> array[<span class="number">0</span>];</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="type">boolean</span> <span class="title function_">offer</span><span class="params">(<span class="type">int</span> offered)</span> &#123;</span><br><span class="line">        <span class="keyword">if</span> (size == array.length) &#123;</span><br><span class="line">            <span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        up(offered);</span><br><span class="line">        size++;</span><br><span class="line">        <span class="keyword">return</span> <span class="literal">true</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">replace</span><span class="params">(<span class="type">int</span> replaced)</span> &#123;</span><br><span class="line">        array[<span class="number">0</span>] = replaced;</span><br><span class="line">        down(<span class="number">0</span>);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">void</span> <span class="title function_">up</span><span class="params">(<span class="type">int</span> offered)</span> &#123;</span><br><span class="line">        <span class="type">int</span> <span class="variable">child</span> <span class="operator">=</span> size;</span><br><span class="line">        <span class="keyword">while</span> (child &gt; <span class="number">0</span>) &#123;</span><br><span class="line">            <span class="type">int</span> <span class="variable">parent</span> <span class="operator">=</span> (child - <span class="number">1</span>) &gt;&gt; <span class="number">1</span>;</span><br><span class="line">            <span class="keyword">if</span> (offered &lt; array[parent]) &#123;</span><br><span class="line">                array[child] = array[parent];</span><br><span class="line">            &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">                <span class="keyword">break</span>;</span><br><span class="line">            &#125;</span><br><span class="line">            child = parent;</span><br><span class="line">        &#125;</span><br><span class="line">        array[child] = offered;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">void</span> <span class="title function_">down</span><span class="params">(<span class="type">int</span> parent)</span> &#123;</span><br><span class="line">        <span class="type">int</span> <span class="variable">left</span> <span class="operator">=</span> (parent &lt;&lt; <span class="number">1</span>) + <span class="number">1</span>;</span><br><span class="line">        <span class="type">int</span> <span class="variable">right</span> <span class="operator">=</span> left + <span class="number">1</span>;</span><br><span class="line">        <span class="type">int</span> <span class="variable">min</span> <span class="operator">=</span> parent;</span><br><span class="line">        <span class="keyword">if</span> (left &lt; size &amp;&amp; array[left] &lt; array[min]) &#123;</span><br><span class="line">            min = left;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">if</span> (right &lt; size &amp;&amp; array[right] &lt; array[min]) &#123;</span><br><span class="line">            min = right;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">if</span> (min != parent) &#123;</span><br><span class="line">            swap(min, parent);</span><br><span class="line">            down(min);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 交换两个索引处的元素</span></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">void</span> <span class="title function_">swap</span><span class="params">(<span class="type">int</span> i, <span class="type">int</span> j)</span> &#123;</span><br><span class="line">        <span class="type">int</span> <span class="variable">t</span> <span class="operator">=</span> array[i];</span><br><span class="line">        array[i] = array[j];</span><br><span class="line">        array[j] = t;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>题解</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="type">int</span> <span class="title function_">findKthLargest</span><span class="params">(<span class="type">int</span>[] numbers, <span class="type">int</span> k)</span> &#123;</span><br><span class="line">    <span class="type">MinHeap</span> <span class="variable">heap</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">MinHeap</span>(k);</span><br><span class="line">    <span class="keyword">for</span> (<span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> <span class="number">0</span>; i &lt; k; i++) &#123;</span><br><span class="line">        heap.offer(numbers[i]);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">for</span> (<span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> k; i &lt; numbers.length; i++) &#123;</span><br><span class="line">        <span class="keyword">if</span>(numbers[i] &gt; heap.peek())&#123;</span><br><span class="line">            heap.replace(numbers[i]);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> heap.peek();</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<blockquote>
<p>求数组中的第 K 大元素，使用堆并不是最佳选择，可以采用快速选择算法</p>
</blockquote>
<h3 id="E03-数据流中第K大元素-力扣-703-题"><a href="#E03-数据流中第K大元素-力扣-703-题" class="headerlink" title="E03. 数据流中第K大元素-力扣 703 题"></a>E03. 数据流中第K大元素-力扣 703 题</h3><p>上题的小顶堆加一个方法</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">MinHeap</span> &#123;</span><br><span class="line">    <span class="comment">// ...</span></span><br><span class="line">	<span class="keyword">public</span> <span class="type">boolean</span> <span class="title function_">isFull</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> size == array.length;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>题解</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">KthLargest</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> MinHeap heap;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="title function_">KthLargest</span><span class="params">(<span class="type">int</span> k, <span class="type">int</span>[] nums)</span> &#123;</span><br><span class="line">        heap = <span class="keyword">new</span> <span class="title class_">MinHeap</span>(k);</span><br><span class="line">        <span class="keyword">for</span>(<span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> <span class="number">0</span>; i &lt; nums.length; i++) &#123;</span><br><span class="line">            add(nums[i]);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">public</span> <span class="type">int</span> <span class="title function_">add</span><span class="params">(<span class="type">int</span> val)</span> &#123;</span><br><span class="line">        <span class="keyword">if</span>(!heap.isFull())&#123;</span><br><span class="line">            heap.offer(val);</span><br><span class="line">        &#125; <span class="keyword">else</span> <span class="keyword">if</span>(val &gt; heap.peek())&#123;</span><br><span class="line">            heap.replace(val);</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> heap.peek();</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<blockquote>
<p>求数据流中的第 K 大元素，使用堆最合适不过</p>
</blockquote>
<h3 id="E04-数据流的中位数-力扣-295-题"><a href="#E04-数据流的中位数-力扣-295-题" class="headerlink" title="E04. 数据流的中位数-力扣 295 题"></a>E04. 数据流的中位数-力扣 295 题</h3><p>可以扩容的 heap, max 用于指定是大顶堆还是小顶堆</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Heap</span> &#123;</span><br><span class="line">    <span class="type">int</span>[] array;</span><br><span class="line">    <span class="type">int</span> size;</span><br><span class="line">    <span class="type">boolean</span> max;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="type">int</span> <span class="title function_">size</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> size;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="title function_">Heap</span><span class="params">(<span class="type">int</span> capacity, <span class="type">boolean</span> max)</span> &#123;</span><br><span class="line">        <span class="built_in">this</span>.array = <span class="keyword">new</span> <span class="title class_">int</span>[capacity];</span><br><span class="line">        <span class="built_in">this</span>.max = max;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 获取堆顶元素</span></span><br><span class="line"><span class="comment">     *</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@return</span> 堆顶元素</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">public</span> <span class="type">int</span> <span class="title function_">peek</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> array[<span class="number">0</span>];</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 删除堆顶元素</span></span><br><span class="line"><span class="comment">     *</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@return</span> 堆顶元素</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">public</span> <span class="type">int</span> <span class="title function_">poll</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="type">int</span> <span class="variable">top</span> <span class="operator">=</span> array[<span class="number">0</span>];</span><br><span class="line">        swap(<span class="number">0</span>, size - <span class="number">1</span>);</span><br><span class="line">        size--;</span><br><span class="line">        down(<span class="number">0</span>);</span><br><span class="line">        <span class="keyword">return</span> top;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 删除指定索引处元素</span></span><br><span class="line"><span class="comment">     *</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> index 索引</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@return</span> 被删除元素</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">public</span> <span class="type">int</span> <span class="title function_">poll</span><span class="params">(<span class="type">int</span> index)</span> &#123;</span><br><span class="line">        <span class="type">int</span> <span class="variable">deleted</span> <span class="operator">=</span> array[index];</span><br><span class="line">        swap(index, size - <span class="number">1</span>);</span><br><span class="line">        size--;</span><br><span class="line">        down(index);</span><br><span class="line">        <span class="keyword">return</span> deleted;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 替换堆顶元素</span></span><br><span class="line"><span class="comment">     *</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> replaced 新元素</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">replace</span><span class="params">(<span class="type">int</span> replaced)</span> &#123;</span><br><span class="line">        array[<span class="number">0</span>] = replaced;</span><br><span class="line">        down(<span class="number">0</span>);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 堆的尾部添加元素</span></span><br><span class="line"><span class="comment">     *</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> offered 新元素</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">offer</span><span class="params">(<span class="type">int</span> offered)</span> &#123;</span><br><span class="line">        <span class="keyword">if</span> (size == array.length) &#123;</span><br><span class="line">            grow();</span><br><span class="line">        &#125;</span><br><span class="line">        up(offered);</span><br><span class="line">        size++;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">void</span> <span class="title function_">grow</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="type">int</span> <span class="variable">capacity</span> <span class="operator">=</span> size + (size &gt;&gt; <span class="number">1</span>);</span><br><span class="line">        <span class="type">int</span>[] newArray = <span class="keyword">new</span> <span class="title class_">int</span>[capacity];</span><br><span class="line">        System.arraycopy(array, <span class="number">0</span>,</span><br><span class="line">                newArray, <span class="number">0</span>, size);</span><br><span class="line">        array = newArray;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 将 offered 元素上浮: 直至 offered 小于父元素或到堆顶</span></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">void</span> <span class="title function_">up</span><span class="params">(<span class="type">int</span> offered)</span> &#123;</span><br><span class="line">        <span class="type">int</span> <span class="variable">child</span> <span class="operator">=</span> size;</span><br><span class="line">        <span class="keyword">while</span> (child &gt; <span class="number">0</span>) &#123;</span><br><span class="line">            <span class="type">int</span> <span class="variable">parent</span> <span class="operator">=</span> (child - <span class="number">1</span>) / <span class="number">2</span>;</span><br><span class="line">            <span class="type">boolean</span> <span class="variable">cmp</span> <span class="operator">=</span> max ? offered &gt; array[parent] : offered &lt; array[parent];</span><br><span class="line">            <span class="keyword">if</span> (cmp) &#123;</span><br><span class="line">                array[child] = array[parent];</span><br><span class="line">            &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">                <span class="keyword">break</span>;</span><br><span class="line">            &#125;</span><br><span class="line">            child = parent;</span><br><span class="line">        &#125;</span><br><span class="line">        array[child] = offered;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="title function_">Heap</span><span class="params">(<span class="type">int</span>[] array, <span class="type">boolean</span> max)</span> &#123;</span><br><span class="line">        <span class="built_in">this</span>.array = array;</span><br><span class="line">        <span class="built_in">this</span>.size = array.length;</span><br><span class="line">        <span class="built_in">this</span>.max = max;</span><br><span class="line">        heapify();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 建堆</span></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">void</span> <span class="title function_">heapify</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="comment">// 如何找到最后这个非叶子节点  size / 2 - 1</span></span><br><span class="line">        <span class="keyword">for</span> (<span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> size / <span class="number">2</span> - <span class="number">1</span>; i &gt;= <span class="number">0</span>; i--) &#123;</span><br><span class="line">            down(i);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 将 parent 索引处的元素下潜: 与两个孩子较大者交换, 直至没孩子或孩子没它大</span></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">void</span> <span class="title function_">down</span><span class="params">(<span class="type">int</span> parent)</span> &#123;</span><br><span class="line">        <span class="type">int</span> <span class="variable">left</span> <span class="operator">=</span> parent * <span class="number">2</span> + <span class="number">1</span>;</span><br><span class="line">        <span class="type">int</span> <span class="variable">right</span> <span class="operator">=</span> left + <span class="number">1</span>;</span><br><span class="line">        <span class="type">int</span> <span class="variable">min</span> <span class="operator">=</span> parent;</span><br><span class="line">        <span class="keyword">if</span> (left &lt; size &amp;&amp; (max ? array[left] &gt; array[min] : array[left] &lt; array[min])) &#123;</span><br><span class="line">            min = left;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">if</span> (right &lt; size &amp;&amp; (max ? array[right] &gt; array[min] : array[right] &lt; array[min])) &#123;</span><br><span class="line">            min = right;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">if</span> (min != parent) &#123; <span class="comment">// 找到了更大的孩子</span></span><br><span class="line">            swap(min, parent);</span><br><span class="line">            down(min);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 交换两个索引处的元素</span></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">void</span> <span class="title function_">swap</span><span class="params">(<span class="type">int</span> i, <span class="type">int</span> j)</span> &#123;</span><br><span class="line">        <span class="type">int</span> <span class="variable">t</span> <span class="operator">=</span> array[i];</span><br><span class="line">        array[i] = array[j];</span><br><span class="line">        array[j] = t;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>题解</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">private</span> <span class="type">Heap</span> <span class="variable">left</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Heap</span>(<span class="number">10</span>, <span class="literal">false</span>);</span><br><span class="line"><span class="keyword">private</span> <span class="type">Heap</span> <span class="variable">right</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Heap</span>(<span class="number">10</span>, <span class="literal">true</span>);</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> 为了保证两边数据量的平衡</span></span><br><span class="line"><span class="comment"> &lt;ul&gt;</span></span><br><span class="line"><span class="comment">  &lt;li&gt;两边数据一样时,加入左边&lt;/li&gt;</span></span><br><span class="line"><span class="comment">  &lt;li&gt;两边数据不一样时,加入右边&lt;/li&gt;</span></span><br><span class="line"><span class="comment"> &lt;/ul&gt;</span></span><br><span class="line"><span class="comment"> 但是, 随便一个数能直接加入吗?</span></span><br><span class="line"><span class="comment"> &lt;ul&gt;</span></span><br><span class="line"><span class="comment">  &lt;li&gt;加入左边前, 应该挑右边最小的加入&lt;/li&gt;</span></span><br><span class="line"><span class="comment">  &lt;li&gt;加入右边前, 应该挑左边最大的加入&lt;/li&gt;</span></span><br><span class="line"><span class="comment"> &lt;/ul&gt;</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">addNum</span><span class="params">(<span class="type">int</span> num)</span> &#123;</span><br><span class="line">    <span class="keyword">if</span> (left.size() == right.size()) &#123;</span><br><span class="line">        right.offer(num);</span><br><span class="line">        left.offer(right.poll());</span><br><span class="line">    &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">        left.offer(num);</span><br><span class="line">        right.offer(left.poll());</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * &lt;ul&gt;</span></span><br><span class="line"><span class="comment"> *     &lt;li&gt;两边数据一致, 左右各取堆顶元素求平均&lt;/li&gt;</span></span><br><span class="line"><span class="comment"> *     &lt;li&gt;左边多一个, 取左边元素&lt;/li&gt;</span></span><br><span class="line"><span class="comment"> * &lt;/ul&gt;</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">public</span> <span class="type">double</span> <span class="title function_">findMedian</span><span class="params">()</span> &#123;</span><br><span class="line">    <span class="keyword">if</span> (left.size() == right.size()) &#123;</span><br><span class="line">        <span class="keyword">return</span> (left.peek() + right.peek()) / <span class="number">2.0</span>;</span><br><span class="line">    &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> left.peek();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<blockquote>
<p>本题还可以使用平衡二叉搜索树求解，不过代码比两个堆复杂</p>
</blockquote>
<h2 id="3-12-二叉树"><a href="#3-12-二叉树" class="headerlink" title="3.12 二叉树"></a>3.12 二叉树</h2><h3 id="E04-对称二叉树-力扣-101-题"><a href="#E04-对称二叉树-力扣-101-题" class="headerlink" title="E04. 对称二叉树-力扣 101 题"></a>E04. 对称二叉树-力扣 101 题</h3><figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="type">boolean</span> <span class="title function_">isSymmetric</span><span class="params">(TreeNode root)</span> &#123;</span><br><span class="line">    <span class="keyword">return</span> check(root.left, root.right);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="type">boolean</span> <span class="title function_">check</span><span class="params">(TreeNode left, TreeNode right)</span> &#123;</span><br><span class="line">    <span class="comment">// 若同时为 null</span></span><br><span class="line">    <span class="keyword">if</span> (left == <span class="literal">null</span> &amp;&amp; right == <span class="literal">null</span>) &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="literal">true</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">// 若有一个为 null (有上一轮筛选，另一个肯定不为 null)</span></span><br><span class="line">    <span class="keyword">if</span> (left == <span class="literal">null</span> || right == <span class="literal">null</span>) &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">if</span> (left.val != right.val) &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> check(left.left, right.right) &amp;&amp; check(left.right, right.left);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>类似题目：Leetcode 100 题 - 相同的树</p>
<h3 id="E05-二叉树最大深度-力扣-104-题"><a href="#E05-二叉树最大深度-力扣-104-题" class="headerlink" title="E05. 二叉树最大深度-力扣 104 题"></a>E05. 二叉树最大深度-力扣 104 题</h3><p><strong>后序遍历求解</strong></p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="comment">/*</span></span><br><span class="line"><span class="comment">    思路：</span></span><br><span class="line"><span class="comment">    1. 得到左子树深度, 得到右子树深度, 二者最大者加一, 就是本节点深度</span></span><br><span class="line"><span class="comment">    2. 因为需要先得到左右子树深度, 很显然是后序遍历典型应用</span></span><br><span class="line"><span class="comment">    3. 关于深度的定义：从根出发, 离根最远的节点总边数,</span></span><br><span class="line"><span class="comment">        注意: 力扣里的深度定义要多一</span></span><br><span class="line"><span class="comment"></span></span><br><span class="line"><span class="comment">        深度2         深度3         深度1</span></span><br><span class="line"><span class="comment">        1            1            1</span></span><br><span class="line"><span class="comment">       / \          / \</span></span><br><span class="line"><span class="comment">      2   3        2   3</span></span><br><span class="line"><span class="comment">                        \</span></span><br><span class="line"><span class="comment">                         4</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">public</span> <span class="type">int</span> <span class="title function_">maxDepth</span><span class="params">(TreeNode node)</span> &#123;</span><br><span class="line">    <span class="keyword">if</span> (node == <span class="literal">null</span>) &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="number">0</span>; <span class="comment">// 非力扣题目改为返回 -1</span></span><br><span class="line">    &#125;</span><br><span class="line">    <span class="type">int</span> <span class="variable">d1</span> <span class="operator">=</span> maxDepth(node.left);</span><br><span class="line">    <span class="type">int</span> <span class="variable">d2</span> <span class="operator">=</span> maxDepth(node.right);</span><br><span class="line">    <span class="keyword">return</span> Integer.max(d1, d2) + <span class="number">1</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>



<p><strong>后序遍历求解-非递归</strong></p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="comment">/*</span></span><br><span class="line"><span class="comment">    思路：</span></span><br><span class="line"><span class="comment">    1. 使用非递归后序遍历, 栈的最大高度即为最大深度</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">public</span> <span class="type">int</span> <span class="title function_">maxDepth</span><span class="params">(TreeNode root)</span> &#123;</span><br><span class="line">    <span class="type">TreeNode</span> <span class="variable">curr</span> <span class="operator">=</span> root;</span><br><span class="line">    LinkedList&lt;TreeNode&gt; stack = <span class="keyword">new</span> <span class="title class_">LinkedList</span>&lt;&gt;();</span><br><span class="line">    <span class="type">int</span> <span class="variable">max</span> <span class="operator">=</span> <span class="number">0</span>;</span><br><span class="line">    <span class="type">TreeNode</span> <span class="variable">pop</span> <span class="operator">=</span> <span class="literal">null</span>;</span><br><span class="line">    <span class="keyword">while</span> (curr != <span class="literal">null</span> || !stack.isEmpty()) &#123;</span><br><span class="line">        <span class="keyword">if</span> (curr != <span class="literal">null</span>) &#123;</span><br><span class="line">            stack.push(curr);</span><br><span class="line">            <span class="type">int</span> <span class="variable">size</span> <span class="operator">=</span> stack.size();</span><br><span class="line">            <span class="keyword">if</span> (size &gt; max) &#123;</span><br><span class="line">                max = size;</span><br><span class="line">            &#125;</span><br><span class="line">            curr = curr.left;</span><br><span class="line">        &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">            <span class="type">TreeNode</span> <span class="variable">peek</span> <span class="operator">=</span> stack.peek();</span><br><span class="line">            <span class="keyword">if</span>(peek.right == <span class="literal">null</span> || peek.right == pop) &#123;</span><br><span class="line">                pop = stack.pop();</span><br><span class="line">            &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">                curr = peek.right;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> max;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>



<p><strong>层序遍历求解</strong></p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="comment">/*</span></span><br><span class="line"><span class="comment">    思路：</span></span><br><span class="line"><span class="comment">    1. 使用层序遍历, 层数即最大深度</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">public</span> <span class="type">int</span> <span class="title function_">maxDepth</span><span class="params">(TreeNode root)</span> &#123;</span><br><span class="line">    <span class="keyword">if</span>(root == <span class="literal">null</span>) &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    Queue&lt;TreeNode&gt; queue = <span class="keyword">new</span> <span class="title class_">LinkedList</span>&lt;&gt;();</span><br><span class="line">    queue.offer(root);</span><br><span class="line">    <span class="type">int</span> <span class="variable">level</span> <span class="operator">=</span> <span class="number">0</span>;</span><br><span class="line">    <span class="keyword">while</span> (!queue.isEmpty()) &#123;</span><br><span class="line">        level++;</span><br><span class="line">        <span class="type">int</span> <span class="variable">size</span> <span class="operator">=</span> queue.size();</span><br><span class="line">        <span class="keyword">for</span> (<span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> <span class="number">0</span>; i &lt; size; i++) &#123;</span><br><span class="line">            <span class="type">TreeNode</span> <span class="variable">node</span> <span class="operator">=</span> queue.poll();</span><br><span class="line">            <span class="keyword">if</span> (node.left != <span class="literal">null</span>) &#123;</span><br><span class="line">                queue.offer(node.left);</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="keyword">if</span> (node.right != <span class="literal">null</span>) &#123;</span><br><span class="line">                queue.offer(node.right);</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> level;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>



<h3 id="E06-二叉树最小深度-力扣-111-题"><a href="#E06-二叉树最小深度-力扣-111-题" class="headerlink" title="E06. 二叉树最小深度-力扣 111 题"></a>E06. 二叉树最小深度-力扣 111 题</h3><p><strong>后序遍历求解</strong></p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="type">int</span> <span class="title function_">minDepth</span><span class="params">(TreeNode node)</span> &#123;</span><br><span class="line">    <span class="keyword">if</span> (node == <span class="literal">null</span>) &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="type">int</span> <span class="variable">d1</span> <span class="operator">=</span> minDepth(node.left);</span><br><span class="line">    <span class="type">int</span> <span class="variable">d2</span> <span class="operator">=</span> minDepth(node.right);</span><br><span class="line">    <span class="keyword">if</span> (d1 == <span class="number">0</span> || d2 == <span class="number">0</span>) &#123;</span><br><span class="line">        <span class="keyword">return</span> d1 + d2 + <span class="number">1</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> Integer.min(d1, d2) + <span class="number">1</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>相较于求最大深度，应当考虑：</p>
<ul>
<li>当右子树为 null，应当返回左子树深度加一</li>
<li>当左子树为 null，应当返回右子树深度加一</li>
</ul>
<p>上面两种情况满足时，不应该再把为 null 子树的深度 0 参与最小值比较，例如这样</p>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">  1</span><br><span class="line"> /</span><br><span class="line">2</span><br></pre></td></tr></table></figure>

<ul>
<li>正确深度为 2，若把为 null 的右子树的深度 0 考虑进来，会得到错误结果 1</li>
</ul>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">1</span><br><span class="line"> \</span><br><span class="line">  3</span><br><span class="line">   \</span><br><span class="line">    4</span><br></pre></td></tr></table></figure>

<ul>
<li>正确深度为 3，若把为 null 的左子树的深度 0 考虑进来，会得到错误结果 1</li>
</ul>
<p><strong>层序遍历求解</strong></p>
<p>遇到的第一个叶子节点所在层就是最小深度</p>
<p>例如，下面的树遇到的第一个叶子节点 3 所在的层就是最小深度，其他 4，7 等叶子节点深度更深，也更晚遇到</p>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">    1</span><br><span class="line">   / \     </span><br><span class="line">  2   3</span><br><span class="line"> / \</span><br><span class="line">4   5 </span><br><span class="line">   /</span><br><span class="line">  7 </span><br></pre></td></tr></table></figure>

<p>代码</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="type">int</span> <span class="title function_">minDepth</span><span class="params">(TreeNode root)</span> &#123;</span><br><span class="line">    <span class="keyword">if</span>(root == <span class="literal">null</span>) &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    Queue&lt;TreeNode&gt; queue = <span class="keyword">new</span> <span class="title class_">LinkedList</span>&lt;&gt;();</span><br><span class="line">    queue.offer(root);</span><br><span class="line">    <span class="type">int</span> <span class="variable">level</span> <span class="operator">=</span> <span class="number">0</span>;</span><br><span class="line">    <span class="keyword">while</span> (!queue.isEmpty()) &#123;</span><br><span class="line">        level++;</span><br><span class="line">        <span class="type">int</span> <span class="variable">size</span> <span class="operator">=</span> queue.size();</span><br><span class="line">        <span class="keyword">for</span> (<span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> <span class="number">0</span>; i &lt; size; i++) &#123;</span><br><span class="line">            <span class="type">TreeNode</span> <span class="variable">node</span> <span class="operator">=</span> queue.poll();</span><br><span class="line">            <span class="keyword">if</span> (node.left == <span class="literal">null</span> &amp;&amp; node.right == <span class="literal">null</span>) &#123;</span><br><span class="line">                <span class="keyword">return</span> level;</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="keyword">if</span> (node.left != <span class="literal">null</span>) &#123;</span><br><span class="line">                queue.offer(node.left);</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="keyword">if</span> (node.right != <span class="literal">null</span>) &#123;</span><br><span class="line">                queue.offer(node.right);</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> level;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>效率会高于之前后序遍历解法，因为找到第一个叶子节点后，就无需后续的层序遍历了</p>
<h3 id="E07-翻转二叉树-力扣-226-题"><a href="#E07-翻转二叉树-力扣-226-题" class="headerlink" title="E07. 翻转二叉树-力扣 226 题"></a>E07. 翻转二叉树-力扣 226 题</h3><figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> TreeNode <span class="title function_">invertTree</span><span class="params">(TreeNode root)</span> &#123;</span><br><span class="line">    fn(root);</span><br><span class="line">    <span class="keyword">return</span> root;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">private</span> <span class="keyword">void</span> <span class="title function_">fn</span><span class="params">(TreeNode node)</span>&#123;</span><br><span class="line">    <span class="keyword">if</span> (node == <span class="literal">null</span>) &#123;</span><br><span class="line">        <span class="keyword">return</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="type">TreeNode</span> <span class="variable">t</span> <span class="operator">=</span> node.left;</span><br><span class="line">    node.left = node.right;</span><br><span class="line">    node.right = t;</span><br><span class="line">    fn(node.left);</span><br><span class="line">    fn(node.right);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>先交换、再递归或是先递归、再交换都可以</p>
<h3 id="E08-后缀表达式转二叉树"><a href="#E08-后缀表达式转二叉树" class="headerlink" title="E08. 后缀表达式转二叉树"></a>E08. 后缀表达式转二叉树</h3><figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">static</span> <span class="keyword">class</span> <span class="title class_">TreeNode</span> &#123;</span><br><span class="line">    <span class="keyword">public</span> String val;</span><br><span class="line">    <span class="keyword">public</span> TreeNode left;</span><br><span class="line">    <span class="keyword">public</span> TreeNode right;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="title function_">TreeNode</span><span class="params">(String val)</span> &#123;</span><br><span class="line">        <span class="built_in">this</span>.val = val;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="title function_">TreeNode</span><span class="params">(TreeNode left, String val, TreeNode right)</span> &#123;</span><br><span class="line">        <span class="built_in">this</span>.left = left;</span><br><span class="line">        <span class="built_in">this</span>.val = val;</span><br><span class="line">        <span class="built_in">this</span>.right = right;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> String <span class="title function_">toString</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="built_in">this</span>.val;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/*</span></span><br><span class="line"><span class="comment">    中缀表达式           (2-1)*3</span></span><br><span class="line"><span class="comment">    后缀（逆波兰）表达式   21-3*</span></span><br><span class="line"><span class="comment"></span></span><br><span class="line"><span class="comment">    1.遇到数字入栈</span></span><br><span class="line"><span class="comment">    2.遇到运算符, 出栈两次, 与当前节点建立父子关系, 当前节点入栈</span></span><br><span class="line"><span class="comment"></span></span><br><span class="line"><span class="comment">    栈</span></span><br><span class="line"><span class="comment">    |   |</span></span><br><span class="line"><span class="comment">    |   |</span></span><br><span class="line"><span class="comment">    |   |</span></span><br><span class="line"><span class="comment">    _____</span></span><br><span class="line"><span class="comment"></span></span><br><span class="line"><span class="comment">    表达式树</span></span><br><span class="line"><span class="comment">        *</span></span><br><span class="line"><span class="comment">       / \</span></span><br><span class="line"><span class="comment">      -   3</span></span><br><span class="line"><span class="comment">     / \</span></span><br><span class="line"><span class="comment">    2   1</span></span><br><span class="line"><span class="comment"></span></span><br><span class="line"><span class="comment">    21-3*</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">public</span> TreeNode <span class="title function_">constructExpressionTree</span><span class="params">(String[] tokens)</span> &#123;</span><br><span class="line">    LinkedList&lt;TreeNode&gt; stack = <span class="keyword">new</span> <span class="title class_">LinkedList</span>&lt;&gt;();</span><br><span class="line">    <span class="keyword">for</span> (String t : tokens) &#123;</span><br><span class="line">        <span class="keyword">switch</span> (t) &#123;</span><br><span class="line">            <span class="keyword">case</span> <span class="string">&quot;+&quot;</span>, <span class="string">&quot;-&quot;</span>, <span class="string">&quot;*&quot;</span>, <span class="string">&quot;/&quot;</span> -&gt; &#123; <span class="comment">// 运算符</span></span><br><span class="line">                <span class="type">TreeNode</span> <span class="variable">right</span> <span class="operator">=</span> stack.pop();</span><br><span class="line">                <span class="type">TreeNode</span> <span class="variable">left</span> <span class="operator">=</span> stack.pop();</span><br><span class="line">                <span class="type">TreeNode</span> <span class="variable">parent</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">TreeNode</span>(t);</span><br><span class="line">                parent.left = left;</span><br><span class="line">                parent.right = right;</span><br><span class="line">                stack.push(parent);</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="keyword">default</span> -&gt; &#123; <span class="comment">// 数字</span></span><br><span class="line">                stack.push(<span class="keyword">new</span> <span class="title class_">TreeNode</span>(t));</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> stack.peek();</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>



<h3 id="E09-根据前序与中序遍历结果构造二叉树-力扣-105-题"><a href="#E09-根据前序与中序遍历结果构造二叉树-力扣-105-题" class="headerlink" title="E09. 根据前序与中序遍历结果构造二叉树-力扣 105 题"></a>E09. 根据前序与中序遍历结果构造二叉树-力扣 105 题</h3><ul>
<li>先通过前序遍历结果定位根节点</li>
<li>再结合中序遍历结果切分左右子树</li>
</ul>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">E09Leetcode105</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/*</span></span><br><span class="line"><span class="comment">        preOrder = &#123;1,2,4,3,6,7&#125;</span></span><br><span class="line"><span class="comment">        inOrder = &#123;4,2,1,6,3,7&#125;</span></span><br><span class="line"><span class="comment"></span></span><br><span class="line"><span class="comment">        根 1</span></span><br><span class="line"><span class="comment">            pre         in</span></span><br><span class="line"><span class="comment">        左  2,4         4,2</span></span><br><span class="line"><span class="comment">        右  3,6,7       6,3,7</span></span><br><span class="line"><span class="comment"></span></span><br><span class="line"><span class="comment"></span></span><br><span class="line"><span class="comment">        根 2</span></span><br><span class="line"><span class="comment">        左 4</span></span><br><span class="line"><span class="comment"></span></span><br><span class="line"><span class="comment">        根 3</span></span><br><span class="line"><span class="comment">        左 6</span></span><br><span class="line"><span class="comment">        右 7</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> TreeNode <span class="title function_">buildTree</span><span class="params">(<span class="type">int</span>[] preOrder, <span class="type">int</span>[] inOrder)</span> &#123;</span><br><span class="line">        <span class="keyword">if</span> (preOrder.length == <span class="number">0</span>) &#123;</span><br><span class="line">            <span class="keyword">return</span> <span class="literal">null</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="comment">// 创建根节点</span></span><br><span class="line">        <span class="type">int</span> <span class="variable">rootValue</span> <span class="operator">=</span> preOrder[<span class="number">0</span>];</span><br><span class="line">        <span class="type">TreeNode</span> <span class="variable">root</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">TreeNode</span>(rootValue);</span><br><span class="line">        <span class="comment">// 区分左右子树</span></span><br><span class="line">        <span class="keyword">for</span> (<span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> <span class="number">0</span>; i &lt; inOrder.length; i++) &#123;</span><br><span class="line">            <span class="keyword">if</span> (inOrder[i] == rootValue) &#123;</span><br><span class="line">                <span class="comment">// 0 ~ i-1 左子树</span></span><br><span class="line">                <span class="comment">// i+1 ~ inOrder.length -1 右子树</span></span><br><span class="line">                <span class="type">int</span>[] inLeft = Arrays.copyOfRange(inOrder, <span class="number">0</span>, i); <span class="comment">// [4,2]</span></span><br><span class="line">                <span class="type">int</span>[] inRight = Arrays.copyOfRange(inOrder, i + <span class="number">1</span>, inOrder.length); <span class="comment">// [6,3,7]</span></span><br><span class="line"></span><br><span class="line">                <span class="type">int</span>[] preLeft = Arrays.copyOfRange(preOrder, <span class="number">1</span>, i + <span class="number">1</span>); <span class="comment">// [2,4]</span></span><br><span class="line">                <span class="type">int</span>[] preRight = Arrays.copyOfRange(preOrder, i + <span class="number">1</span>, inOrder.length); <span class="comment">// [3,6,7]</span></span><br><span class="line"></span><br><span class="line">                root.left = buildTree(preLeft, inLeft); <span class="comment">// 2</span></span><br><span class="line">                root.right = buildTree(preRight, inRight); <span class="comment">// 3</span></span><br><span class="line">                <span class="keyword">break</span>;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> root;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<ul>
<li>代码可以进一步优化，涉及新数据结构，以后实现</li>
</ul>
<h3 id="E10-根据中序与后序遍历结果构造二叉树-力扣-106-题"><a href="#E10-根据中序与后序遍历结果构造二叉树-力扣-106-题" class="headerlink" title="E10. 根据中序与后序遍历结果构造二叉树-力扣 106 题"></a>E10. 根据中序与后序遍历结果构造二叉树-力扣 106 题</h3><ul>
<li>先通过后序遍历结果定位根节点</li>
<li>再结合中序遍历结果切分左右子树</li>
</ul>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> TreeNode <span class="title function_">buildTree</span><span class="params">(<span class="type">int</span>[] inOrder, <span class="type">int</span>[] postOrder)</span> &#123;</span><br><span class="line">    <span class="keyword">if</span> (inOrder.length == <span class="number">0</span>) &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="literal">null</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">// 根</span></span><br><span class="line">    <span class="type">int</span> <span class="variable">rootValue</span> <span class="operator">=</span> postOrder[postOrder.length - <span class="number">1</span>];</span><br><span class="line">    <span class="type">TreeNode</span> <span class="variable">root</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">TreeNode</span>(rootValue);</span><br><span class="line">    <span class="comment">// 切分左右子树</span></span><br><span class="line">    <span class="keyword">for</span> (<span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> <span class="number">0</span>; i &lt; inOrder.length; i++) &#123;</span><br><span class="line">        <span class="keyword">if</span> (inOrder[i] == rootValue) &#123;</span><br><span class="line">            <span class="type">int</span>[] inLeft = Arrays.copyOfRange(inOrder, <span class="number">0</span>, i);</span><br><span class="line">            <span class="type">int</span>[] inRight = Arrays.copyOfRange(inOrder, i + <span class="number">1</span>, inOrder.length);</span><br><span class="line"></span><br><span class="line">            <span class="type">int</span>[] postLeft = Arrays.copyOfRange(postOrder, <span class="number">0</span>, i);</span><br><span class="line">            <span class="type">int</span>[] postRight = Arrays.copyOfRange(postOrder, i, postOrder.length - <span class="number">1</span>);</span><br><span class="line"></span><br><span class="line">            root.left = buildTree(inLeft, postLeft);</span><br><span class="line">            root.right = buildTree(inRight, postRight);</span><br><span class="line">            <span class="keyword">break</span>;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> root;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<ul>
<li>代码可以进一步优化，涉及新数据结构，以后实现</li>
</ul>
<h1 id="附录"><a href="#附录" class="headerlink" title="附录"></a>附录</h1><h2 id="参考文章"><a href="#参考文章" class="headerlink" title="参考文章"></a>参考文章</h2><p>[^1]: <a target="_blank" rel="noopener" href="https://www.merriam-webster.com/dictionary/algorithm">“Definition of ALGORITHM”</a>. <em>Merriam-Webster Online Dictionary</em>. <a target="_blank" rel="noopener" href="https://web.archive.org/web/20200214074446/https://www.merriam-webster.com/dictionary/algorithm">Archived</a> from the original on February 14, 2020. Retrieved November 14, 2019.<br>[^2]: Introduction to Algorithm 中文译作《算法导论》<br>[^3]: 主要参考文档 <a target="_blank" rel="noopener" href="https://en.wikipedia.org/wiki/Binary_search_algorithm">https://en.wikipedia.org/wiki/Binary_search_algorithm</a><br>[^4]: 图片及概念均摘自 Introduction to Algorithm 4th，3.1节，3.2 节<br>[^5]: 图片引用自 wikipedia linkedlist 条目，<a target="_blank" rel="noopener" href="https://en.wikipedia.org/wiki/Linked_list">https://en.wikipedia.org/wiki/Linked_list</a></p>
<p>[^6]: 也称为 Pascal’s triangle <a target="_blank" rel="noopener" href="https://en.wikipedia.org/wiki/Pascal%27s_triangle">https://en.wikipedia.org/wiki/Pascal%27s_triangle</a></p>
<p>[^7]: <a target="_blank" rel="noopener" href="https://zhuanlan.zhihu.com/p/257214075">递归求解斐波那契数列的时间复杂度——几种简洁证明 - 知乎 (zhihu.com)</a><br>[^8]: Fibonacci 介绍：<a target="_blank" rel="noopener" href="https://en.wikipedia.org/wiki/Fibonacci_number">https://en.wikipedia.org/wiki/Fibonacci_number</a><br>[^9]: <a target="_blank" rel="noopener" href="https://zhuanlan.zhihu.com/p/165877869">几种计算Fibonacci数列算法的时间复杂度比较 - 知乎 (zhihu.com)</a><br>[^10]: 几种斐波那契数列算法比较 <a target="_blank" rel="noopener" href="https://www.nayuki.io/page/fast-fibonacci-algorithms">Fast Fibonacci algorithms (nayuki.io)</a></p>
<p>[^11]: 我知道的有 C++，Scala<br>[^12]: jdk 版本有关，64 位 jdk，按 8 字节对齐<br>[^13]: 汉诺塔图片资料均来自 <a target="_blank" rel="noopener" href="https://en.wikipedia.org/wiki/Tower_of_Hanoi">https://en.wikipedia.org/wiki/Tower_of_Hanoi</a><br>[^14]: 与主定理类似的还有 Akra–Bazzi method，<a target="_blank" rel="noopener" href="https://en.wikipedia.org/wiki/Akra%E2%80%93Bazzi_method">https://en.wikipedia.org/wiki/Akra%E2%80%93Bazzi_method</a></p>
<p>[^15]: 龟兔赛跑动画来自于 <a target="_blank" rel="noopener" href="https://onestepcode.com/floyd-hare-tortoise-algorithm-demo/">Floyd’s Hare and Tortoise Algorithm Demo - One Step! Code (onestepcode.com)</a></p>
<p>[^16]: Josephus problem 主要参考 <a target="_blank" rel="noopener" href="https://en.wikipedia.org/wiki/Josephus_problem">https://en.wikipedia.org/wiki/Josephus_problem</a></p>
<h2 id="落选题目"><a href="#落选题目" class="headerlink" title="落选题目"></a>落选题目</h2><h3 id="反转字符数组"><a href="#反转字符数组" class="headerlink" title="反转字符数组"></a>反转字符数组</h3><figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> &#123;</span><br><span class="line">    <span class="type">char</span>[] array = <span class="string">&quot;abcde&quot;</span>.toCharArray();</span><br><span class="line">    recursion(array, <span class="number">0</span>, array.length - <span class="number">1</span>);</span><br><span class="line">    System.out.println(Arrays.toString(array));</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">recursion</span><span class="params">(<span class="type">char</span>[] array, <span class="type">int</span> i, <span class="type">int</span> j)</span> &#123;</span><br><span class="line">    <span class="keyword">if</span> (i &gt;= j) &#123;</span><br><span class="line">        <span class="keyword">return</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    swap(array, i, j);</span><br><span class="line">    recursion(array, ++i, --j);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">swap</span><span class="params">(<span class="type">char</span>[] array, <span class="type">int</span> i, <span class="type">int</span> j)</span> &#123;</span><br><span class="line">    <span class="type">char</span> <span class="variable">c</span> <span class="operator">=</span> array[i];</span><br><span class="line">    array[i] = array[j];</span><br><span class="line">    array[j] = c;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<ul>
<li>第一次交换的是 array[0] 和 array[4]</li>
<li>第二次交换的是 array[1] 和 array[3]</li>
<li>第三次 i &#x3D; j &#x3D; 2，开始返回</li>
<li>如果 array.length 是偶数，则会在 i &gt; j 时返回</li>
</ul>
<h2 id="力扣高评价题目列表"><a href="#力扣高评价题目列表" class="headerlink" title="力扣高评价题目列表"></a>力扣高评价题目列表</h2><p>引用自 <a target="_blank" rel="noopener" href="https://zhuanlan.zhihu.com/p/449686402">面试最常考的 100 道算法题分类整理！ - 知乎 (zhihu.com)</a></p>
<blockquote>
<p>带 ✔️ 是本课程讲解过的</p>
</blockquote>
<ul>
<li><strong><a href="https://link.zhihu.com/?target=https://leetcode-cn.com/problems/two-sum/">1. Two Sum (两数之和)</a></strong>, Easy, 11757 likes ✔️</li>
<li><strong><a href="https://link.zhihu.com/?target=https://leetcode-cn.com/problems/add-two-numbers/">2. Add Two Numbers (两数相加)</a></strong>, Medium, 6524 likes</li>
<li><strong><a href="https://link.zhihu.com/?target=https://leetcode-cn.com/problems/longest-substring-without-repeating-characters/">3. Longest Substring Without Repeating Characters (无重复字符的最长子串)</a></strong>, Medium, 5845 likes ✔️</li>
<li><strong><a href="https://link.zhihu.com/?target=https://leetcode-cn.com/problems/median-of-two-sorted-arrays/">4. Median of Two Sorted Arrays (寻找两个正序数组的中位数)</a></strong>, Hard, 4303 likes</li>
<li><strong><a href="https://link.zhihu.com/?target=https://leetcode-cn.com/problems/longest-palindromic-substring/">5. Longest Palindromic Substring (最长回文子串)</a></strong>, Medium, 3896 likes</li>
<li><strong><a href="https://link.zhihu.com/?target=https://leetcode-cn.com/problems/3sum/">15. 3Sum (三数之和)</a></strong>, Medium, 3582 likes</li>
<li><strong><a href="https://link.zhihu.com/?target=https://leetcode-cn.com/problems/maximum-subarray/">53. Maximum Subarray (最大子序和)</a></strong>, Easy, 3533 likes</li>
<li><strong><a href="https://link.zhihu.com/?target=https://leetcode-cn.com/problems/reverse-integer/">7. Reverse Integer (整数反转)</a></strong>, Easy, 2970 likes</li>
<li><strong><a href="https://link.zhihu.com/?target=https://leetcode-cn.com/problems/container-with-most-water/">11. Container With Most Water (盛最多水的容器)</a></strong>, Medium, 2659 likes</li>
<li><strong><a href="https://link.zhihu.com/?target=https://leetcode-cn.com/problems/trapping-rain-water/">42. Trapping Rain Water (接雨水)</a></strong>, Hard, 2552 likes</li>
<li><strong><a href="https://link.zhihu.com/?target=https://leetcode-cn.com/problems/valid-parentheses/">20. Valid Parentheses (有效的括号)</a></strong>, Easy, 2544 likes ✔️</li>
<li><strong><a href="https://link.zhihu.com/?target=https://leetcode-cn.com/problems/regular-expression-matching/">10. Regular Expression Matching (正则表达式匹配)</a></strong>, Hard, 2273 likes</li>
<li><strong><a href="https://link.zhihu.com/?target=https://leetcode-cn.com/problems/remove-duplicates-from-sorted-array/">26. Remove Duplicates from Sorted Array (删除有序数组中的重复项)</a></strong>, Easy, 2146 likes ✔️</li>
<li><strong><a href="https://link.zhihu.com/?target=https://leetcode-cn.com/problems/single-number/">136. Single Number (只出现一次的数字)</a></strong>, Easy, 1958 likes ✔️</li>
<li><strong><a href="https://link.zhihu.com/?target=https://leetcode-cn.com/problems/generate-parentheses/">22. Generate Parentheses (括号生成)</a></strong>, Medium, 1946 likes</li>
<li><strong><a href="https://link.zhihu.com/?target=https://leetcode-cn.com/problems/reverse-linked-list/">206. Reverse Linked List (反转链表)</a></strong>, Easy, 1886 likes ✔️</li>
<li><strong><a href="https://link.zhihu.com/?target=https://leetcode-cn.com/problems/merge-two-sorted-lists/">21. Merge Two Sorted Lists (合并两个有序链表)</a></strong>, Easy, 1832 likes ✔️</li>
<li><strong><a href="https://link.zhihu.com/?target=https://leetcode-cn.com/problems/climbing-stairs/">70. Climbing Stairs (爬楼梯)</a></strong>, Easy, 1791 likes ✔️</li>
<li><strong><a href="https://link.zhihu.com/?target=https://leetcode-cn.com/problems/longest-increasing-subsequence/">300. Longest Increasing Subsequence (最长递增子序列)</a></strong>, Medium, 1773 likes</li>
<li><strong><a href="https://link.zhihu.com/?target=https://leetcode-cn.com/problems/best-time-to-buy-and-sell-stock/">121. Best Time to Buy and Sell Stock (买卖股票的最佳时机)</a></strong>, Easy, 1766 likes</li>
<li><strong><a href="https://link.zhihu.com/?target=https://leetcode-cn.com/problems/edit-distance/">72. Edit Distance (编辑距离)</a></strong>, Hard, 1743 likes</li>
<li><strong><a href="https://link.zhihu.com/?target=https://leetcode-cn.com/problems/longest-common-prefix/">14. Longest Common Prefix (最长公共前缀)</a></strong>, Easy, 1707 likes</li>
<li><strong><a href="https://link.zhihu.com/?target=https://leetcode-cn.com/problems/house-robber/">198. House Robber (打家劫舍)</a></strong>, Medium, 1585 likes</li>
<li><strong><a href="https://link.zhihu.com/?target=https://leetcode-cn.com/problems/palindrome-number/">9. Palindrome Number (回文数)</a></strong>, Easy, 1568 likes</li>
<li><strong><a href="https://link.zhihu.com/?target=https://leetcode-cn.com/problems/lru-cache/">146. LRU Cache (LRU 缓存机制)</a></strong>, Medium, 1544 likes</li>
<li><strong><a href="https://link.zhihu.com/?target=https://leetcode-cn.com/problems/remove-nth-node-from-end-of-list/">19. Remove Nth Node From End of List (删除链表的倒数第 N 个结点)</a></strong>, Medium, 1494 likes ✔️</li>
<li><strong><a href="https://link.zhihu.com/?target=https://leetcode-cn.com/problems/search-in-rotated-sorted-array/">33. Search in Rotated Sorted Array (搜索旋转排序数组)</a></strong>, Medium, 1493 likes</li>
<li><strong><a href="https://link.zhihu.com/?target=https://leetcode-cn.com/problems/permutations/">46. Permutations (全排列)</a></strong>, Medium, 1484 likes</li>
<li><strong><a href="https://link.zhihu.com/?target=https://leetcode-cn.com/problems/symmetric-tree/">101. Symmetric Tree (对称二叉树)</a></strong>, Easy, 1483 likes ✔️</li>
<li><strong><a href="https://link.zhihu.com/?target=https://leetcode-cn.com/problems/largest-rectangle-in-histogram/">84. Largest Rectangle in Histogram (柱状图中最大的矩形)</a></strong>, Hard, 1472 likes</li>
<li><strong><a href="https://link.zhihu.com/?target=https://leetcode-cn.com/problems/combination-sum/">39. Combination Sum (组合总和)</a></strong>, Medium, 1466 likes</li>
<li><strong><a href="https://link.zhihu.com/?target=https://leetcode-cn.com/problems/roman-to-integer/">13. Roman to Integer (罗马数字转整数)</a></strong>, Easy, 1436 likes</li>
<li><strong><a href="https://link.zhihu.com/?target=https://leetcode-cn.com/problems/merge-k-sorted-lists/">23. Merge k Sorted Lists (合并K个升序链表)</a></strong>, Hard, 1436 likes ✔️</li>
<li><strong><a href="https://link.zhihu.com/?target=https://leetcode-cn.com/problems/letter-combinations-of-a-phone-number/">17. Letter Combinations of a Phone Number (电话号码的字母组合)</a></strong>, Medium, 1436 likes</li>
<li><strong><a href="https://link.zhihu.com/?target=https://leetcode-cn.com/problems/coin-change/">322. Coin Change (零钱兑换)</a></strong>, Medium, 1414 likes</li>
<li><strong><a href="https://link.zhihu.com/?target=https://leetcode-cn.com/problems/longest-valid-parentheses/">32. Longest Valid Parentheses (最长有效括号)</a></strong>, Hard, 1400 likes</li>
<li><strong><a href="https://link.zhihu.com/?target=https://leetcode-cn.com/problems/find-the-duplicate-number/">287. Find the Duplicate Number (寻找重复数)</a></strong>, Medium, 1325 likes</li>
<li><strong><a href="https://link.zhihu.com/?target=https://leetcode-cn.com/problems/best-time-to-buy-and-sell-stock-ii/">122. Best Time to Buy and Sell Stock II (买卖股票的最佳时机 II)</a></strong>, Easy, 1306 likes</li>
<li><strong><a href="https://link.zhihu.com/?target=https://leetcode-cn.com/problems/intersection-of-two-linked-lists/">160. Intersection of Two Linked Lists (相交链表)</a></strong>, Easy, 1302 likes ✔️</li>
<li><strong><a href="https://link.zhihu.com/?target=https://leetcode-cn.com/problems/jump-game/">55. Jump Game (跳跃游戏)</a></strong>, Medium, 1292 likes</li>
<li><strong><a href="https://link.zhihu.com/?target=https://leetcode-cn.com/problems/minimum-window-substring/">76. Minimum Window Substring (最小覆盖子串)</a></strong>, Hard, 1280 likes</li>
<li><strong><a href="https://link.zhihu.com/?target=https://leetcode-cn.com/problems/number-of-islands/">200. Number of Islands (岛屿数量)</a></strong>, Medium, 1270 likes</li>
<li><strong><a href="https://link.zhihu.com/?target=https://leetcode-cn.com/problems/subsets/">78. Subsets (子集)</a></strong>, Medium, 1269 likes</li>
<li><strong><a href="https://link.zhihu.com/?target=https://leetcode-cn.com/problems/next-permutation/">31. Next Permutation (下一个排列)</a></strong>, Medium, 1260 likes</li>
<li><strong><a href="https://link.zhihu.com/?target=https://leetcode-cn.com/problems/unique-binary-search-trees/">96. Unique Binary Search Trees (不同的二叉搜索树)</a></strong>, Medium, 1257 likes</li>
<li><strong><a href="https://link.zhihu.com/?target=https://leetcode-cn.com/problems/sort-list/">148. Sort List (排序链表)</a></strong>, Medium, 1248 likes</li>
<li><strong><a href="https://link.zhihu.com/?target=https://leetcode-cn.com/problems/lowest-common-ancestor-of-a-binary-tree/">236. Lowest Common Ancestor of a Binary Tree (二叉树的最近公共祖先)</a></strong>, Medium, 1238 likes ✔️</li>
<li><strong><a href="https://link.zhihu.com/?target=https://leetcode-cn.com/problems/reverse-nodes-in-k-group/">25. Reverse Nodes in k-Group (K 个一组翻转链表)</a></strong>, Hard, 1230 likes</li>
<li><strong><a href="https://link.zhihu.com/?target=https://leetcode-cn.com/problems/zigzag-conversion/">6. ZigZag Conversion (Z 字形变换)</a></strong>, Medium, 1226 likes</li>
<li><strong><a href="https://link.zhihu.com/?target=https://leetcode-cn.com/problems/maximum-product-subarray/">152. Maximum Product Subarray (乘积最大子数组)</a></strong>, Medium, 1223 likes</li>
<li><strong><a href="https://link.zhihu.com/?target=https://leetcode-cn.com/problems/kth-largest-element-in-an-array/">215. Kth Largest Element in an Array (数组中的第K个最大元素)</a></strong>, Medium, 1211 likes ✔️</li>
<li><strong><a href="https://link.zhihu.com/?target=https://leetcode-cn.com/problems/string-to-integer-atoi/">8. String to Integer (atoi) (字符串转换整数 (atoi))</a></strong>, Medium, 1168 likes</li>
<li><strong><a href="https://link.zhihu.com/?target=https://leetcode-cn.com/problems/first-missing-positive/">41. First Missing Positive (缺失的第一个正数)</a></strong>, Hard, 1163 likes</li>
<li><strong><a href="https://link.zhihu.com/?target=https://leetcode-cn.com/problems/move-zeroes/">283. Move Zeroes (移动零)</a></strong>, Easy, 1162 likes</li>
<li><strong><a href="https://link.zhihu.com/?target=https://leetcode-cn.com/problems/linked-list-cycle/">141. Linked List Cycle (环形链表)</a></strong>, Easy, 1161 likes ✔️</li>
<li><strong><a href="https://link.zhihu.com/?target=https://leetcode-cn.com/problems/validate-binary-search-tree/">98. Validate Binary Search Tree (验证二叉搜索树)</a></strong>, Medium, 1156 likes ✔️</li>
<li><strong><a href="https://link.zhihu.com/?target=https://leetcode-cn.com/problems/binary-tree-maximum-path-sum/">124. Binary Tree Maximum Path Sum (二叉树中的最大路径和)</a></strong>, Hard, 1152 likes</li>
<li><strong><a href="https://link.zhihu.com/?target=https://leetcode-cn.com/problems/construct-binary-tree-from-preorder-and-inorder-traversal/">105. Construct Binary Tree from Preorder and Inorder Traversal (从前序与中序遍历序列构造二叉树)</a></strong>, Medium, 1149 likes ✔️</li>
<li><strong><a href="https://link.zhihu.com/?target=https://leetcode-cn.com/problems/find-first-and-last-position-of-element-in-sorted-array/">34. Find First and Last Position of Element in Sorted Array (在排序数组中查找元素的第一个和最后一个位置)</a></strong>, Medium, 1137 likes ✔️</li>
<li><strong><a href="https://link.zhihu.com/?target=https://leetcode-cn.com/problems/sliding-window-maximum/">239. Sliding Window Maximum (滑动窗口最大值)</a></strong>, Hard, 1114 likes</li>
<li><strong><a href="https://link.zhihu.com/?target=https://leetcode-cn.com/problems/linked-list-cycle-ii/">142. Linked List Cycle II (环形链表 II)</a></strong>, Medium, 1097 likes ✔️</li>
<li><strong><a href="https://link.zhihu.com/?target=https://leetcode-cn.com/problems/word-break/">139. Word Break (单词拆分)</a></strong>, Medium, 1097 likes</li>
<li><strong><a href="https://link.zhihu.com/?target=https://leetcode-cn.com/problems/jump-game-ii/">45. Jump Game II (跳跃游戏 II)</a></strong>, Medium, 1094 likes</li>
<li><strong><a href="https://link.zhihu.com/?target=https://leetcode-cn.com/problems/majority-element/">169. Majority Element (多数元素)</a></strong>, Easy, 1089 likes</li>
<li><strong><a href="https://link.zhihu.com/?target=https://leetcode-cn.com/problems/palindrome-linked-list/">234. Palindrome Linked List (回文链表)</a></strong>, Easy, 1072 likes ✔️</li>
<li><strong><a href="https://link.zhihu.com/?target=https://leetcode-cn.com/problems/unique-paths/">62. Unique Paths (不同路径)</a></strong>, Medium, 1072 likes</li>
<li><strong><a href="https://link.zhihu.com/?target=https://leetcode-cn.com/problems/rotate-array/">189. Rotate Array (旋转数组)</a></strong>, Medium, 1057 likes</li>
<li><strong><a href="https://link.zhihu.com/?target=https://leetcode-cn.com/problems/binary-tree-inorder-traversal/">94. Binary Tree Inorder Traversal (二叉树的中序遍历)</a></strong>, Easy, 1052 likes ✔️</li>
<li><strong><a href="https://link.zhihu.com/?target=https://leetcode-cn.com/problems/merge-intervals/">56. Merge Intervals (合并区间)</a></strong>, Medium, 1051 likes</li>
<li><strong><a href="https://link.zhihu.com/?target=https://leetcode-cn.com/problems/merge-sorted-array/">88. Merge Sorted Array (合并两个有序数组)</a></strong>, Easy, 1041 likes ✔️</li>
<li><strong><a href="https://link.zhihu.com/?target=https://leetcode-cn.com/problems/subarray-sum-equals-k/">560. Subarray Sum Equals K (和为K的子数组)</a></strong>, Medium, 1036 likes</li>
<li><strong><a href="https://link.zhihu.com/?target=https://leetcode-cn.com/problems/perfect-squares/">279. Perfect Squares (完全平方数)</a></strong>, Medium, 1035 likes</li>
<li><strong><a href="https://link.zhihu.com/?target=https://leetcode-cn.com/problems/search-insert-position/">35. Search Insert Position (搜索插入位置)</a></strong>, Easy, 1005 likes</li>
<li><strong><a href="https://link.zhihu.com/?target=https://leetcode-cn.com/problems/swap-nodes-in-pairs/">24. Swap Nodes in Pairs (两两交换链表中的节点)</a></strong>, Medium, 996 likes</li>
<li><strong><a href="https://link.zhihu.com/?target=https://leetcode-cn.com/problems/maximal-rectangle/">85. Maximal Rectangle (最大矩形)</a></strong>, Hard, 983 likes</li>
<li><strong><a href="https://link.zhihu.com/?target=https://leetcode-cn.com/problems/implement-strstr/">28. Implement strStr() (实现 strStr())</a></strong>, Easy, 982 likes</li>
<li><strong><a href="https://link.zhihu.com/?target=https://leetcode-cn.com/problems/reverse-linked-list-ii/">92. Reverse Linked List II (反转链表 II)</a></strong>, Medium, 980 likes</li>
<li><strong><a href="https://link.zhihu.com/?target=https://leetcode-cn.com/problems/min-stack/">155. Min Stack (最小栈)</a></strong>, Easy, 979 likes</li>
<li><strong><a href="https://link.zhihu.com/?target=https://leetcode-cn.com/problems/word-search/">79. Word Search (单词搜索)</a></strong>, Medium, 979 likes</li>
<li><strong><a href="https://link.zhihu.com/?target=https://leetcode-cn.com/problems/remove-element/">27. Remove Element (移除元素)</a></strong>, Easy, 967 likes</li>
<li><strong><a href="https://link.zhihu.com/?target=https://leetcode-cn.com/problems/n-queens/">51. N-Queens (N 皇后)</a></strong>, Hard, 965 likes</li>
<li><strong><a href="https://link.zhihu.com/?target=https://leetcode-cn.com/problems/sort-colors/">75. Sort Colors (颜色分类)</a></strong>, Medium, 961 likes</li>
<li><strong><a href="https://link.zhihu.com/?target=https://leetcode-cn.com/problems/binary-tree-level-order-traversal/">102. Binary Tree Level Order Traversal (二叉树的层序遍历)</a></strong>, Medium, 960 likes ✔️</li>
<li><strong><a href="https://link.zhihu.com/?target=https://leetcode-cn.com/problems/rotate-image/">48. Rotate Image (旋转图像)</a></strong>, Medium, 960 likes</li>
<li><strong><a href="https://link.zhihu.com/?target=https://leetcode-cn.com/problems/unique-binary-search-trees-ii/">95. Unique Binary Search Trees II (不同的二叉搜索树 II)</a></strong>, Medium, 955 likes</li>
<li><strong><a href="https://link.zhihu.com/?target=https://leetcode-cn.com/problems/minimum-path-sum/">64. Minimum Path Sum (最小路径和)</a></strong>, Medium, 954 likes</li>
<li><strong><a href="https://link.zhihu.com/?target=https://leetcode-cn.com/problems/queue-reconstruction-by-height/">406. Queue Reconstruction by Height (根据身高重建队列)</a></strong>, Medium, 947 likes</li>
<li><strong><a href="https://link.zhihu.com/?target=https://leetcode-cn.com/problems/invert-binary-tree/">226. Invert Binary Tree (翻转二叉树)</a></strong>, Easy, 941 likes ✔️</li>
<li><strong><a href="https://link.zhihu.com/?target=https://leetcode-cn.com/problems/path-sum-iii/">437. Path Sum III (路径总和 III)</a></strong>, Medium, 937 likes</li>
<li><strong><a href="https://link.zhihu.com/?target=https://leetcode-cn.com/problems/maximum-depth-of-binary-tree/">104. Maximum Depth of Binary Tree (二叉树的最大深度)</a></strong>, Easy, 937 likes ✔️</li>
<li><strong><a href="https://link.zhihu.com/?target=https://leetcode-cn.com/problems/delete-node-in-a-linked-list/">237. Delete Node in a Linked List (删除链表中的节点)</a></strong>, Easy, 936 likes ✔️</li>
<li><strong><a href="https://link.zhihu.com/?target=https://leetcode-cn.com/problems/house-robber-iii/">337. House Robber III (打家劫舍 III)</a></strong>, Medium, 929 likes</li>
<li><strong><a href="https://link.zhihu.com/?target=https://leetcode-cn.com/problems/4sum/">18. 4Sum (四数之和)</a></strong>, Medium, 918 likes</li>
<li><strong><a href="https://link.zhihu.com/?target=https://leetcode-cn.com/problems/decode-ways/">91. Decode Ways (解码方法)</a></strong>, Medium, 904 likes</li>
<li><strong><a href="https://link.zhihu.com/?target=https://leetcode-cn.com/problems/course-schedule/">207. Course Schedule (课程表)</a></strong>, Medium, 897 likes</li>
<li><strong><a href="https://link.zhihu.com/?target=https://leetcode-cn.com/problems/sudoku-solver/">37. Sudoku Solver (解数独)</a></strong>, Hard, 897 likes</li>
<li><strong><a href="https://link.zhihu.com/?target=https://leetcode-cn.com/problems/combine-two-tables/">175. Combine Two Tables (组合两个表)</a></strong>, Easy, 891 likes</li>
<li><strong><a href="https://link.zhihu.com/?target=https://leetcode-cn.com/problems/partition-equal-subset-sum/">416. Partition Equal Subset Sum (分割等和子集)</a></strong>, Medium, 886 likes</li>
<li><strong><a href="https://link.zhihu.com/?target=https://leetcode-cn.com/problems/product-of-array-except-self/">238. Product of Array Except Self (除自身以外数组的乘积)</a></strong>, Medium, 885 likes</li>
<li><strong><a href="https://link.zhihu.com/?target=https://leetcode-cn.com/problems/flatten-binary-tree-to-linked-list/">114. Flatten Binary Tree to Linked List (二叉树展开为链表)</a></strong>, Medium, 877 likes ✔️</li>
</ul>
</article><div class="post-copyright"><div class="post-copyright__author"><span class="post-copyright-meta">文章作者: </span><span class="post-copyright-info"><a href="https://jhcgnb.cn">GISer</a></span></div><div class="post-copyright__type"><span class="post-copyright-meta">文章链接: </span><span class="post-copyright-info"><a href="https://jhcgnb.cn/posts/239d0cb7/">https://jhcgnb.cn/posts/239d0cb7/</a></span></div><div class="post-copyright__notice"><span class="post-copyright-meta">版权声明: </span><span class="post-copyright-info">本博客所有文章除特别声明外，均采用 <a href="https://creativecommons.org/licenses/by-nc-sa/4.0/" target="_blank">CC BY-NC-SA 4.0</a> 许可协议。转载请注明来自 <a href="https://jhcgnb.cn" target="_blank">纸飞机的旅行</a>！</span></div></div><div class="tag_share"><div class="post-meta__tag-list"><a class="post-meta__tags" href="/tags/%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84%E4%B8%8E%E7%AE%97%E6%B3%951/">数据结构与算法1</a></div><div class="post_share"><div class="social-share" data-image="http://rx6zk4j2b.hn-bkt.clouddn.com/blogs/image-20230110084245095.png" data-sites="facebook,twitter,wechat,weibo,qq"></div><link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/butterfly-extsrc/sharejs/dist/css/share.min.css" media="print" onload="this.media='all'"><script src="https://cdn.jsdelivr.net/npm/butterfly-extsrc/sharejs/dist/js/social-share.min.js" defer></script></div></div><div class="post-reward"><div class="reward-button"><i class="fas fa-qrcode"></i> 打赏</div><div class="reward-main"><ul class="reward-all"><li class="reward-item"><a href="/img/wechat.jpg" target="_blank"><img class="post-qr-code-img" src= "" data-lazy-src="/img/wechat.jpg" alt="wechat"/></a><div class="post-qr-code-desc">wechat</div></li><li class="reward-item"><a href="/img/alipay.jpg" target="_blank"><img class="post-qr-code-img" src= "" data-lazy-src="/img/alipay.jpg" alt="alipay"/></a><div class="post-qr-code-desc">alipay</div></li></ul></div></div><nav class="pagination-post" id="pagination"><div class="prev-post pull-left"><a href="/posts/65ce02f4/" title="Delaunay三角网"><img class="cover" src= "" data-lazy-src="/img/category_img.jpg" onerror="onerror=null;src='/img/404.jpg'" alt="cover of previous post"><div class="pagination-info"><div class="label">上一篇</div><div class="prev_info">Delaunay三角网</div></div></a></div><div class="next-post pull-right"><a href="/posts/4cbe9020/" title="9.NodeJS"><img class="cover" src= "" data-lazy-src="http://rx6zk4j2b.hn-bkt.clouddn.com/blogs/image-20230722213120050.png" onerror="onerror=null;src='/img/404.jpg'" alt="cover of next post"><div class="pagination-info"><div class="label">下一篇</div><div class="next_info">9.NodeJS</div></div></a></div></nav><hr class="custom-hr"/><div id="post-comment"><div class="comment-head"><div class="comment-headline"><i class="fas fa-comments fa-fw"></i><span> 评论</span></div></div><div class="comment-wrap"><div><div id="lv-container" data-id="city" data-uid="MTAyMC81ODY2OC8zNTEzMA=="></div></div></div></div></div><div class="aside-content" id="aside-content"><div class="card-widget card-info"><div class="is-center"><div class="avatar-img"><img src= "" data-lazy-src="/img/adaver.png" onerror="this.onerror=null;this.src='/img/friend_404.gif'" alt="avatar"/></div><div class="author-info__name">GISer</div><div class="author-info__description">选择有时候比努力更重要</div></div><div class="card-info-data site-data is-center"><a href="/archives/"><div class="headline">文章</div><div class="length-num">20</div></a><a href="/tags/"><div class="headline">标签</div><div class="length-num">7</div></a><a href="/categories/"><div class="headline">分类</div><div class="length-num">3</div></a></div><a id="card-info-btn" target="_blank" rel="noopener" href="https://github.com/Jinhe404"><i class="fab fa-github"></i><span>Follow Me</span></a><div class="card-info-social-icons is-center"><a class="social-icon" href="https://github.com/Jinhe404" target="_blank" title="Github"><i class="fab fa-github" style="color: #24292e;"></i></a><a class="social-icon" href="/jhcgnb@163.com" target="_blank" title="Email"><i class="fas fa-envelope" style="color: #4a7dbe;"></i></a><a class="social-icon" href="https://blog.csdn.net/weixin_44857463?type=blog" target="_blank" title="CSDN"><i class="fa fa-book-open"></i></a><a class="social-icon" href="tencent://AddContact/?fromId=45&amp;fromSubId=1&amp;subcmd=all&amp;uin=31025287432&amp;website=www.oicqzone.com" target="_blank" title="QQ"><i class="fab fa-qq"></i></a></div></div><div class="card-widget card-announcement"><div class="item-headline"><i class="fas fa-bullhorn fa-shake"></i><span>公告</span></div><div class="announcement_content">This is my Blog</div></div><div class="sticky_layout"><div class="card-widget" id="card-toc"><div class="item-headline"><i class="fas fa-stream"></i><span>目录</span><span class="toc-percentage"></span></div><div class="toc-content"><ol class="toc"><li class="toc-item toc-level-1"><a class="toc-link" href="#%E4%B8%80-%E5%88%9D%E8%AF%86%E7%AE%97%E6%B3%95"><span class="toc-number">1.</span> <span class="toc-text">一. 初识算法</span></a><ol class="toc-child"><li class="toc-item toc-level-2"><a class="toc-link" href="#1-1-%E4%BB%80%E4%B9%88%E6%98%AF%E7%AE%97%E6%B3%95%EF%BC%9F"><span class="toc-number">1.1.</span> <span class="toc-text">1.1 什么是算法？</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#1-2-%E4%BB%80%E4%B9%88%E6%98%AF%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84%EF%BC%9F"><span class="toc-number">1.2.</span> <span class="toc-text">1.2 什么是数据结构？</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#1-3-%E4%BA%8C%E5%88%86%E6%9F%A5%E6%89%BE-3"><span class="toc-number">1.3.</span> <span class="toc-text">1.3 二分查找 [^3]</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#%E4%BA%8C%E5%88%86%E6%9F%A5%E6%89%BE%E5%9F%BA%E7%A1%80%E7%89%88"><span class="toc-number">1.3.1.</span> <span class="toc-text">二分查找基础版</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E4%BA%8C%E5%88%86%E6%9F%A5%E6%89%BE%E6%94%B9%E5%8F%98%E7%89%88"><span class="toc-number">1.3.2.</span> <span class="toc-text">二分查找改变版</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E8%A1%A1%E9%87%8F%E7%AE%97%E6%B3%95%E5%A5%BD%E5%9D%8F"><span class="toc-number">1.3.3.</span> <span class="toc-text">衡量算法好坏</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E4%BA%8C%E5%88%86%E6%9F%A5%E6%89%BE%E5%B9%B3%E8%A1%A1%E7%89%88"><span class="toc-number">1.3.4.</span> <span class="toc-text">二分查找平衡版</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E4%BA%8C%E5%88%86%E6%9F%A5%E6%89%BE-Java-%E7%89%88"><span class="toc-number">1.3.5.</span> <span class="toc-text">二分查找 Java 版</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#Leftmost-%E4%B8%8E-Rightmost"><span class="toc-number">1.3.6.</span> <span class="toc-text">Leftmost 与 Rightmost</span></a></li></ol></li></ol></li><li class="toc-item toc-level-1"><a class="toc-link" href="#%E4%BA%8C-%E5%9F%BA%E7%A1%80%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84"><span class="toc-number">2.</span> <span class="toc-text">二. 基础数据结构</span></a><ol class="toc-child"><li class="toc-item toc-level-2"><a class="toc-link" href="#2-1-%E6%95%B0%E7%BB%84"><span class="toc-number">2.1.</span> <span class="toc-text">2.1 数组</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#%E6%A6%82%E8%BF%B0"><span class="toc-number">2.1.1.</span> <span class="toc-text">概述</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E5%8A%A8%E6%80%81%E6%95%B0%E7%BB%84"><span class="toc-number">2.1.2.</span> <span class="toc-text">动态数组</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E4%BA%8C%E7%BB%B4%E6%95%B0%E7%BB%84"><span class="toc-number">2.1.3.</span> <span class="toc-text">二维数组</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E5%B1%80%E9%83%A8%E6%80%A7%E5%8E%9F%E7%90%86"><span class="toc-number">2.1.4.</span> <span class="toc-text">局部性原理</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E8%B6%8A%E7%95%8C%E6%A3%80%E6%9F%A5"><span class="toc-number">2.1.5.</span> <span class="toc-text">越界检查</span></a></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#2-2-%E9%93%BE%E8%A1%A8"><span class="toc-number">2.2.</span> <span class="toc-text">2.2 链表</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#%E6%A6%82%E8%BF%B0-1"><span class="toc-number">2.2.1.</span> <span class="toc-text">概述</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E5%8D%95%E5%90%91%E9%93%BE%E8%A1%A8"><span class="toc-number">2.2.2.</span> <span class="toc-text">单向链表</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E5%8D%95%E5%90%91%E9%93%BE%E8%A1%A8%EF%BC%88%E5%B8%A6%E5%93%A8%E5%85%B5%EF%BC%89"><span class="toc-number">2.2.3.</span> <span class="toc-text">单向链表（带哨兵）</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E5%8F%8C%E5%90%91%E9%93%BE%E8%A1%A8%EF%BC%88%E5%B8%A6%E5%93%A8%E5%85%B5%EF%BC%89"><span class="toc-number">2.2.4.</span> <span class="toc-text">双向链表（带哨兵）</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E7%8E%AF%E5%BD%A2%E9%93%BE%E8%A1%A8%EF%BC%88%E5%B8%A6%E5%93%A8%E5%85%B5%EF%BC%89"><span class="toc-number">2.2.5.</span> <span class="toc-text">环形链表（带哨兵）</span></a></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#2-3-%E9%80%92%E5%BD%92"><span class="toc-number">2.3.</span> <span class="toc-text">2.3 递归</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#%E6%A6%82%E8%BF%B0-2"><span class="toc-number">2.3.1.</span> <span class="toc-text">概述</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E5%8D%95%E8%B7%AF%E9%80%92%E5%BD%92-Single-Recursion"><span class="toc-number">2.3.2.</span> <span class="toc-text">单路递归 Single Recursion</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E5%A4%9A%E8%B7%AF%E9%80%92%E5%BD%92-Multi-Recursion"><span class="toc-number">2.3.3.</span> <span class="toc-text">多路递归 Multi Recursion</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E9%80%92%E5%BD%92%E4%BC%98%E5%8C%96-%E8%AE%B0%E5%BF%86%E6%B3%95"><span class="toc-number">2.3.4.</span> <span class="toc-text">递归优化-记忆法</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E9%80%92%E5%BD%92%E4%BC%98%E5%8C%96-%E5%B0%BE%E9%80%92%E5%BD%92"><span class="toc-number">2.3.5.</span> <span class="toc-text">递归优化-尾递归</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E9%80%92%E5%BD%92%E6%97%B6%E9%97%B4%E5%A4%8D%E6%9D%82%E5%BA%A6-Master-theorem-14"><span class="toc-number">2.3.6.</span> <span class="toc-text">递归时间复杂度-Master theorem[^14]</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E9%80%92%E5%BD%92%E6%97%B6%E9%97%B4%E5%A4%8D%E6%9D%82%E5%BA%A6-%E5%B1%95%E5%BC%80%E6%B1%82%E8%A7%A3"><span class="toc-number">2.3.7.</span> <span class="toc-text">递归时间复杂度-展开求解</span></a></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#2-4-%E9%98%9F%E5%88%97"><span class="toc-number">2.4.</span> <span class="toc-text">2.4 队列</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#%E6%A6%82%E8%BF%B0-3"><span class="toc-number">2.4.1.</span> <span class="toc-text">概述</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E9%93%BE%E8%A1%A8%E5%AE%9E%E7%8E%B0"><span class="toc-number">2.4.2.</span> <span class="toc-text">链表实现</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E7%8E%AF%E5%BD%A2%E6%95%B0%E7%BB%84%E5%AE%9E%E7%8E%B0"><span class="toc-number">2.4.3.</span> <span class="toc-text">环形数组实现</span></a></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#2-5-%E6%A0%88"><span class="toc-number">2.5.</span> <span class="toc-text">2.5 栈</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#%E6%A6%82%E8%BF%B0-4"><span class="toc-number">2.5.1.</span> <span class="toc-text">概述</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E9%93%BE%E8%A1%A8%E5%AE%9E%E7%8E%B0-1"><span class="toc-number">2.5.2.</span> <span class="toc-text">链表实现</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E6%95%B0%E7%BB%84%E5%AE%9E%E7%8E%B0"><span class="toc-number">2.5.3.</span> <span class="toc-text">数组实现</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E5%BA%94%E7%94%A8"><span class="toc-number">2.5.4.</span> <span class="toc-text">应用</span></a></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#2-6-%E5%8F%8C%E7%AB%AF%E9%98%9F%E5%88%97"><span class="toc-number">2.6.</span> <span class="toc-text">2.6 双端队列</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#%E6%A6%82%E8%BF%B0-5"><span class="toc-number">2.6.1.</span> <span class="toc-text">概述</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E9%93%BE%E8%A1%A8%E5%AE%9E%E7%8E%B0-2"><span class="toc-number">2.6.2.</span> <span class="toc-text">链表实现</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E6%95%B0%E7%BB%84%E5%AE%9E%E7%8E%B0-1"><span class="toc-number">2.6.3.</span> <span class="toc-text">数组实现</span></a></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#2-7-%E4%BC%98%E5%85%88%E7%BA%A7%E9%98%9F%E5%88%97"><span class="toc-number">2.7.</span> <span class="toc-text">2.7 优先级队列</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#%E6%97%A0%E5%BA%8F%E6%95%B0%E7%BB%84%E5%AE%9E%E7%8E%B0"><span class="toc-number">2.7.1.</span> <span class="toc-text">无序数组实现</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E6%9C%89%E5%BA%8F%E6%95%B0%E7%BB%84%E5%AE%9E%E7%8E%B0"><span class="toc-number">2.7.2.</span> <span class="toc-text">有序数组实现</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E5%A0%86%E5%AE%9E%E7%8E%B0"><span class="toc-number">2.7.3.</span> <span class="toc-text">堆实现</span></a></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#2-8-%E9%98%BB%E5%A1%9E%E9%98%9F%E5%88%97"><span class="toc-number">2.8.</span> <span class="toc-text">2.8 阻塞队列</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#%E5%8D%95%E9%94%81%E5%AE%9E%E7%8E%B0"><span class="toc-number">2.8.1.</span> <span class="toc-text">单锁实现</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E5%8F%8C%E9%94%81%E5%AE%9E%E7%8E%B0"><span class="toc-number">2.8.2.</span> <span class="toc-text">双锁实现</span></a></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#2-9-%E5%A0%86"><span class="toc-number">2.9.</span> <span class="toc-text">2.9 堆</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#2-10-%E4%BA%8C%E5%8F%89%E6%A0%91"><span class="toc-number">2.10.</span> <span class="toc-text">2.10 二叉树</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#%E5%AD%98%E5%82%A8"><span class="toc-number">2.10.1.</span> <span class="toc-text">存储</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E9%81%8D%E5%8E%86"><span class="toc-number">2.10.2.</span> <span class="toc-text">遍历</span></a><ol class="toc-child"><li class="toc-item toc-level-4"><a class="toc-link" href="#%E5%B9%BF%E5%BA%A6%E4%BC%98%E5%85%88"><span class="toc-number">2.10.2.1.</span> <span class="toc-text">广度优先</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#%E6%B7%B1%E5%BA%A6%E4%BC%98%E5%85%88"><span class="toc-number">2.10.2.2.</span> <span class="toc-text">深度优先</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#%E9%80%92%E5%BD%92%E5%AE%9E%E7%8E%B0"><span class="toc-number">2.10.2.3.</span> <span class="toc-text">递归实现</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#%E9%9D%9E%E9%80%92%E5%BD%92%E5%AE%9E%E7%8E%B0"><span class="toc-number">2.10.2.4.</span> <span class="toc-text">非递归实现</span></a></li></ol></li></ol></li></ol></li><li class="toc-item toc-level-1"><a class="toc-link" href="#%E4%B8%89-%E7%BB%83%E4%B9%A0"><span class="toc-number">3.</span> <span class="toc-text">三. 练习</span></a><ol class="toc-child"><li class="toc-item toc-level-2"><a class="toc-link" href="#3-1-%E6%97%B6%E9%97%B4%E5%A4%8D%E6%9D%82%E5%BA%A6"><span class="toc-number">3.1.</span> <span class="toc-text">3.1 时间复杂度</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#3-2-%E4%BA%8C%E5%88%86%E6%9F%A5%E6%89%BE"><span class="toc-number">3.2.</span> <span class="toc-text">3.2 二分查找</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#E01-%E4%BA%8C%E5%88%86%E6%9F%A5%E6%89%BE-%E5%8A%9B%E6%89%A3-704-%E9%A2%98"><span class="toc-number">3.2.1.</span> <span class="toc-text">E01. 二分查找-力扣 704 题</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#E02-%E6%90%9C%E7%B4%A2%E6%8F%92%E5%85%A5%E4%BD%8D%E7%BD%AE-%E5%8A%9B%E6%89%A3-35-%E9%A2%98"><span class="toc-number">3.2.2.</span> <span class="toc-text">E02. 搜索插入位置-力扣 35 题</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#E03-%E6%90%9C%E7%B4%A2%E5%BC%80%E5%A7%8B%E7%BB%93%E6%9D%9F%E4%BD%8D%E7%BD%AE-%E5%8A%9B%E6%89%A3-34-%E9%A2%98"><span class="toc-number">3.2.3.</span> <span class="toc-text">E03. 搜索开始结束位置-力扣 34 题</span></a></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#3-3-%E9%80%92%E5%BD%92-single-recursion"><span class="toc-number">3.3.</span> <span class="toc-text">3.3 递归 - single recursion</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#E03-%E4%BA%8C%E5%88%86%E6%9F%A5%E6%89%BE"><span class="toc-number">3.3.1.</span> <span class="toc-text">E03. 二分查找</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#E04-%E5%86%92%E6%B3%A1%E6%8E%92%E5%BA%8F"><span class="toc-number">3.3.2.</span> <span class="toc-text">E04. 冒泡排序</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#E05-%E6%8F%92%E5%85%A5%E6%8E%92%E5%BA%8F"><span class="toc-number">3.3.3.</span> <span class="toc-text">E05. 插入排序</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#E06-%E7%BA%A6%E7%91%9F%E5%A4%AB%E9%97%AE%E9%A2%98-16"><span class="toc-number">3.3.4.</span> <span class="toc-text">E06. 约瑟夫问题[^16]</span></a></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#3-4-%E9%80%92%E5%BD%92-multi-recursion"><span class="toc-number">3.4.</span> <span class="toc-text">3.4 递归 - multi recursion</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#E02-%E6%B1%89%E8%AF%BA%E5%A1%94-13"><span class="toc-number">3.4.1.</span> <span class="toc-text">E02. 汉诺塔[^13]</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#E03-%E6%9D%A8%E8%BE%89%E4%B8%89%E8%A7%92-6"><span class="toc-number">3.4.2.</span> <span class="toc-text">E03. 杨辉三角[^6]</span></a></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#3-5-%E9%93%BE%E8%A1%A8"><span class="toc-number">3.5.</span> <span class="toc-text">3.5 链表</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#E01-%E5%8F%8D%E8%BD%AC%E5%8D%95%E5%90%91%E9%93%BE%E8%A1%A8-%E5%8A%9B%E6%89%A3-206-%E9%A2%98"><span class="toc-number">3.5.1.</span> <span class="toc-text">E01. 反转单向链表-力扣 206 题</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#E02-%E6%A0%B9%E6%8D%AE%E5%80%BC%E5%88%A0%E9%99%A4%E8%8A%82%E7%82%B9-%E5%8A%9B%E6%89%A3-203-%E9%A2%98"><span class="toc-number">3.5.2.</span> <span class="toc-text">E02. 根据值删除节点-力扣 203 题</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#E03-%E5%88%A0%E9%99%A4%E5%80%92%E6%95%B0%E8%8A%82%E7%82%B9-%E5%8A%9B%E6%89%A3-19-%E9%A2%98"><span class="toc-number">3.5.3.</span> <span class="toc-text">E03.  删除倒数节点-力扣 19 题</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#E04-%E6%9C%89%E5%BA%8F%E9%93%BE%E8%A1%A8%E5%8E%BB%E9%87%8D-%E5%8A%9B%E6%89%A3-83-%E9%A2%98"><span class="toc-number">3.5.4.</span> <span class="toc-text">E04. 有序链表去重-力扣 83 题</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#E05-%E6%9C%89%E5%BA%8F%E9%93%BE%E8%A1%A8%E5%8E%BB%E9%87%8D-%E5%8A%9B%E6%89%A3-82-%E9%A2%98"><span class="toc-number">3.5.5.</span> <span class="toc-text">E05. 有序链表去重-力扣 82 题</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#E06-%E5%90%88%E5%B9%B6%E6%9C%89%E5%BA%8F%E9%93%BE%E8%A1%A8-%E5%8A%9B%E6%89%A3-21-%E9%A2%98"><span class="toc-number">3.5.6.</span> <span class="toc-text">E06. 合并有序链表-力扣 21 题</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#E07-%E5%90%88%E5%B9%B6%E5%A4%9A%E4%B8%AA%E6%9C%89%E5%BA%8F%E9%93%BE%E8%A1%A8-%E5%8A%9B%E6%89%A3-23-%E9%A2%98"><span class="toc-number">3.5.7.</span> <span class="toc-text">E07. 合并多个有序链表-力扣 23 题</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#E08-%E6%9F%A5%E6%89%BE%E9%93%BE%E8%A1%A8%E4%B8%AD%E9%97%B4%E8%8A%82%E7%82%B9-%E5%8A%9B%E6%89%A3-876-%E9%A2%98"><span class="toc-number">3.5.8.</span> <span class="toc-text">E08. 查找链表中间节点-力扣 876 题</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#E09-%E5%9B%9E%E6%96%87%E9%93%BE%E8%A1%A8-%E5%8A%9B%E6%89%A3-234-%E9%A2%98"><span class="toc-number">3.5.9.</span> <span class="toc-text">E09. 回文链表-力扣 234 题</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#E10-%E7%8E%AF%E5%BD%A2%E9%93%BE%E8%A1%A8-%E5%8A%9B%E6%89%A3-141-%E9%A2%98"><span class="toc-number">3.5.10.</span> <span class="toc-text">E10. 环形链表-力扣 141 题</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#E11-%E7%8E%AF%E5%BD%A2%E9%93%BE%E8%A1%A8-%E5%8A%9B%E6%89%A3-142-%E9%A2%98"><span class="toc-number">3.5.11.</span> <span class="toc-text">E11. 环形链表-力扣 142 题</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#Ex1-%E5%88%A0%E9%99%A4%E8%8A%82%E7%82%B9-%E5%8A%9B%E6%89%A3-237-%E9%A2%98"><span class="toc-number">3.5.12.</span> <span class="toc-text">Ex1. 删除节点-力扣 237 题</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#Ex2-%E5%85%B1%E5%B0%BE%E9%93%BE%E8%A1%A8-%E5%8A%9B%E6%89%A3-160-%E9%A2%98"><span class="toc-number">3.5.13.</span> <span class="toc-text">Ex2. 共尾链表-力扣 160 题</span></a></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#3-6-%E6%95%B0%E7%BB%84"><span class="toc-number">3.6.</span> <span class="toc-text">3.6 数组</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#E01-%E5%90%88%E5%B9%B6%E6%9C%89%E5%BA%8F%E6%95%B0%E7%BB%84"><span class="toc-number">3.6.1.</span> <span class="toc-text">E01. 合并有序数组</span></a></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#3-7-%E9%98%9F%E5%88%97"><span class="toc-number">3.7.</span> <span class="toc-text">3.7 队列</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#E01-%E4%BA%8C%E5%8F%89%E6%A0%91%E5%B1%82%E5%BA%8F%E9%81%8D%E5%8E%86-%E5%8A%9B%E6%89%A3-102-%E9%A2%98"><span class="toc-number">3.7.1.</span> <span class="toc-text">E01. 二叉树层序遍历-力扣 102 题</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#Ex1-%E8%AE%BE%E8%AE%A1%E9%98%9F%E5%88%97-%E5%8A%9B%E6%89%A3-622-%E9%A2%98"><span class="toc-number">3.7.2.</span> <span class="toc-text">Ex1. 设计队列-力扣 622 题</span></a></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#3-8-%E6%A0%88"><span class="toc-number">3.8.</span> <span class="toc-text">3.8 栈</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#E01-%E6%9C%89%E6%95%88%E7%9A%84%E6%8B%AC%E5%8F%B7-%E5%8A%9B%E6%89%A3-20-%E9%A2%98"><span class="toc-number">3.8.1.</span> <span class="toc-text">E01. 有效的括号-力扣 20 题</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#E02-%E5%90%8E%E7%BC%80%E8%A1%A8%E8%BE%BE%E5%BC%8F%E6%B1%82%E5%80%BC-%E5%8A%9B%E6%89%A3-120-%E9%A2%98"><span class="toc-number">3.8.2.</span> <span class="toc-text">E02. 后缀表达式求值-力扣 120 题</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#E03-%E4%B8%AD%E7%BC%80%E8%A1%A8%E8%BE%BE%E5%BC%8F%E8%BD%AC%E5%90%8E%E7%BC%80"><span class="toc-number">3.8.3.</span> <span class="toc-text">E03. 中缀表达式转后缀</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#E04-%E5%8F%8C%E6%A0%88%E6%A8%A1%E6%8B%9F%E9%98%9F%E5%88%97-%E5%8A%9B%E6%89%A3-232-%E9%A2%98"><span class="toc-number">3.8.4.</span> <span class="toc-text">E04. 双栈模拟队列-力扣 232 题</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#E05-%E5%8D%95%E9%98%9F%E5%88%97%E6%A8%A1%E6%8B%9F%E6%A0%88-%E5%8A%9B%E6%89%A3-225-%E9%A2%98"><span class="toc-number">3.8.5.</span> <span class="toc-text">E05. 单队列模拟栈-力扣 225 题</span></a></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#3-9-%E5%8F%8C%E7%AB%AF%E9%98%9F%E5%88%97"><span class="toc-number">3.9.</span> <span class="toc-text">3.9 双端队列</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#E01-%E4%BA%8C%E5%8F%89%E6%A0%91-Z-%E5%AD%97%E5%B1%82%E5%BA%8F%E9%81%8D%E5%8E%86-%E5%8A%9B%E6%89%A3-103-%E9%A2%98"><span class="toc-number">3.9.1.</span> <span class="toc-text">E01. 二叉树 Z 字层序遍历-力扣 103 题</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#Ex1-%E8%AE%BE%E8%AE%A1%E5%8F%8C%E7%AB%AF%E9%98%9F%E5%88%97-%E5%8A%9B%E6%89%A3-641-%E9%A2%98"><span class="toc-number">3.9.2.</span> <span class="toc-text">Ex1. 设计双端队列-力扣 641 题</span></a></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#3-10-%E4%BC%98%E5%85%88%E7%BA%A7%E9%98%9F%E5%88%97"><span class="toc-number">3.10.</span> <span class="toc-text">3.10 优先级队列</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#E01-%E5%90%88%E5%B9%B6%E5%A4%9A%E4%B8%AA%E6%9C%89%E5%BA%8F%E9%93%BE%E8%A1%A8-%E5%8A%9B%E6%89%A3-23-%E9%A2%98"><span class="toc-number">3.10.1.</span> <span class="toc-text">E01. 合并多个有序链表-力扣 23 题</span></a></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#3-11-%E5%A0%86"><span class="toc-number">3.11.</span> <span class="toc-text">3.11 堆</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#E01-%E5%A0%86%E6%8E%92%E5%BA%8F"><span class="toc-number">3.11.1.</span> <span class="toc-text">E01. 堆排序</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#E02-%E6%95%B0%E7%BB%84%E4%B8%AD%E7%AC%ACK%E5%A4%A7%E5%85%83%E7%B4%A0-%E5%8A%9B%E6%89%A3-215-%E9%A2%98"><span class="toc-number">3.11.2.</span> <span class="toc-text">E02. 数组中第K大元素-力扣 215 题</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#E03-%E6%95%B0%E6%8D%AE%E6%B5%81%E4%B8%AD%E7%AC%ACK%E5%A4%A7%E5%85%83%E7%B4%A0-%E5%8A%9B%E6%89%A3-703-%E9%A2%98"><span class="toc-number">3.11.3.</span> <span class="toc-text">E03. 数据流中第K大元素-力扣 703 题</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#E04-%E6%95%B0%E6%8D%AE%E6%B5%81%E7%9A%84%E4%B8%AD%E4%BD%8D%E6%95%B0-%E5%8A%9B%E6%89%A3-295-%E9%A2%98"><span class="toc-number">3.11.4.</span> <span class="toc-text">E04. 数据流的中位数-力扣 295 题</span></a></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#3-12-%E4%BA%8C%E5%8F%89%E6%A0%91"><span class="toc-number">3.12.</span> <span class="toc-text">3.12 二叉树</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#E04-%E5%AF%B9%E7%A7%B0%E4%BA%8C%E5%8F%89%E6%A0%91-%E5%8A%9B%E6%89%A3-101-%E9%A2%98"><span class="toc-number">3.12.1.</span> <span class="toc-text">E04. 对称二叉树-力扣 101 题</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#E05-%E4%BA%8C%E5%8F%89%E6%A0%91%E6%9C%80%E5%A4%A7%E6%B7%B1%E5%BA%A6-%E5%8A%9B%E6%89%A3-104-%E9%A2%98"><span class="toc-number">3.12.2.</span> <span class="toc-text">E05. 二叉树最大深度-力扣 104 题</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#E06-%E4%BA%8C%E5%8F%89%E6%A0%91%E6%9C%80%E5%B0%8F%E6%B7%B1%E5%BA%A6-%E5%8A%9B%E6%89%A3-111-%E9%A2%98"><span class="toc-number">3.12.3.</span> <span class="toc-text">E06. 二叉树最小深度-力扣 111 题</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#E07-%E7%BF%BB%E8%BD%AC%E4%BA%8C%E5%8F%89%E6%A0%91-%E5%8A%9B%E6%89%A3-226-%E9%A2%98"><span class="toc-number">3.12.4.</span> <span class="toc-text">E07. 翻转二叉树-力扣 226 题</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#E08-%E5%90%8E%E7%BC%80%E8%A1%A8%E8%BE%BE%E5%BC%8F%E8%BD%AC%E4%BA%8C%E5%8F%89%E6%A0%91"><span class="toc-number">3.12.5.</span> <span class="toc-text">E08. 后缀表达式转二叉树</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#E09-%E6%A0%B9%E6%8D%AE%E5%89%8D%E5%BA%8F%E4%B8%8E%E4%B8%AD%E5%BA%8F%E9%81%8D%E5%8E%86%E7%BB%93%E6%9E%9C%E6%9E%84%E9%80%A0%E4%BA%8C%E5%8F%89%E6%A0%91-%E5%8A%9B%E6%89%A3-105-%E9%A2%98"><span class="toc-number">3.12.6.</span> <span class="toc-text">E09. 根据前序与中序遍历结果构造二叉树-力扣 105 题</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#E10-%E6%A0%B9%E6%8D%AE%E4%B8%AD%E5%BA%8F%E4%B8%8E%E5%90%8E%E5%BA%8F%E9%81%8D%E5%8E%86%E7%BB%93%E6%9E%9C%E6%9E%84%E9%80%A0%E4%BA%8C%E5%8F%89%E6%A0%91-%E5%8A%9B%E6%89%A3-106-%E9%A2%98"><span class="toc-number">3.12.7.</span> <span class="toc-text">E10. 根据中序与后序遍历结果构造二叉树-力扣 106 题</span></a></li></ol></li></ol></li><li class="toc-item toc-level-1"><a class="toc-link" href="#%E9%99%84%E5%BD%95"><span class="toc-number">4.</span> <span class="toc-text">附录</span></a><ol class="toc-child"><li class="toc-item toc-level-2"><a class="toc-link" href="#%E5%8F%82%E8%80%83%E6%96%87%E7%AB%A0"><span class="toc-number">4.1.</span> <span class="toc-text">参考文章</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#%E8%90%BD%E9%80%89%E9%A2%98%E7%9B%AE"><span class="toc-number">4.2.</span> <span class="toc-text">落选题目</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#%E5%8F%8D%E8%BD%AC%E5%AD%97%E7%AC%A6%E6%95%B0%E7%BB%84"><span class="toc-number">4.2.1.</span> <span class="toc-text">反转字符数组</span></a></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#%E5%8A%9B%E6%89%A3%E9%AB%98%E8%AF%84%E4%BB%B7%E9%A2%98%E7%9B%AE%E5%88%97%E8%A1%A8"><span class="toc-number">4.3.</span> <span class="toc-text">力扣高评价题目列表</span></a></li></ol></li></ol></div></div><div class="card-widget card-recent-post"><div class="item-headline"><i class="fas fa-history"></i><span>最新文章</span></div><div class="aside-list"><div class="aside-list-item"><a class="thumbnail" href="/posts/5a9e107b/" title="11.性能优化"><img src= "" data-lazy-src="http://rx6zk4j2b.hn-bkt.clouddn.com/blogs/image-20230722213120050.png" onerror="this.onerror=null;this.src='/img/404.jpg'" alt="11.性能优化"/></a><div class="content"><a class="title" href="/posts/5a9e107b/" title="11.性能优化">11.性能优化</a><time datetime="2023-10-18T13:51:52.000Z" title="发表于 2023-10-18 21:51:52">2023-10-18</time></div></div><div class="aside-list-item"><a class="thumbnail" href="/posts/467cee4/" title="流形与非流行"><img src= "" data-lazy-src="http://rx6zk4j2b.hn-bkt.clouddn.com/blogs/image-20230708214917843.png" onerror="this.onerror=null;this.src='/img/404.jpg'" alt="流形与非流行"/></a><div class="content"><a class="title" href="/posts/467cee4/" title="流形与非流行">流形与非流行</a><time datetime="2023-07-08T13:45:31.000Z" title="发表于 2023-07-08 21:45:31">2023-07-08</time></div></div><div class="aside-list-item"><a class="thumbnail" href="/posts/f43726f7/" title="详解@RequestParam"><img src= "" data-lazy-src="http://rx6zk4j2b.hn-bkt.clouddn.com/Java-Logo.png" onerror="this.onerror=null;this.src='/img/404.jpg'" alt="详解@RequestParam"/></a><div class="content"><a class="title" href="/posts/f43726f7/" title="详解@RequestParam">详解@RequestParam</a><time datetime="2023-07-04T02:07:55.000Z" title="发表于 2023-07-04 10:07:55">2023-07-04</time></div></div><div class="aside-list-item"><a class="thumbnail" href="/posts/a98e3ede/" title="Cesium面试题"><img src= "" data-lazy-src="/img/category_img.jpg" onerror="this.onerror=null;this.src='/img/404.jpg'" alt="Cesium面试题"/></a><div class="content"><a class="title" href="/posts/a98e3ede/" title="Cesium面试题">Cesium面试题</a><time datetime="2023-07-03T06:07:01.000Z" title="发表于 2023-07-03 14:07:01">2023-07-03</time></div></div><div class="aside-list-item"><a class="thumbnail" href="/posts/d594835b/" title="Linux项目部署详细步骤"><img src= "" data-lazy-src="http://rx6zk4j2b.hn-bkt.clouddn.com/blogs/image-20230709192449145.png" onerror="this.onerror=null;this.src='/img/404.jpg'" alt="Linux项目部署详细步骤"/></a><div class="content"><a class="title" href="/posts/d594835b/" title="Linux项目部署详细步骤">Linux项目部署详细步骤</a><time datetime="2023-05-09T11:08:33.000Z" title="发表于 2023-05-09 19:08:33">2023-05-09</time></div></div></div></div></div></div></main><footer id="footer"><div id="footer-wrap"><div class="copyright">&copy;2020 - 2023 By GISer</div><div class="framework-info"><span>框架 </span><a target="_blank" rel="noopener" href="https://hexo.io">Hexo</a><span class="footer-separator">|</span><span>主题 </span><a target="_blank" rel="noopener" href="https://github.com/jerryc127/hexo-theme-butterfly">Butterfly</a></div></div></footer></div><div id="rightside"><div id="rightside-config-hide"><button id="readmode" type="button" title="阅读模式"><i class="fas fa-book-open"></i></button><button id="darkmode" type="button" title="浅色和深色模式转换"><i class="fas fa-adjust"></i></button><button id="hide-aside-btn" type="button" title="单栏和双栏切换"><i class="fas fa-arrows-alt-h"></i></button></div><div id="rightside-config-show"><button id="rightside_config" type="button" title="设置"><i class="fas fa-cog fa-spin"></i></button><button class="close" id="mobile-toc-button" type="button" title="目录"><i class="fas fa-list-ul"></i></button><a id="to_comment" href="#post-comment" title="直达评论"><i class="fas fa-comments"></i></a><button id="go-up" type="button" title="回到顶部"><span class="scroll-percent"></span><i class="fas fa-arrow-up"></i></button></div></div><div><script src="/js/utils.js"></script><script src="/js/main.js"></script><script src="https://cdn.jsdelivr.net/npm/@fancyapps/ui/dist/fancybox/fancybox.umd.min.js"></script><script src="https://cdn.jsdelivr.net/npm/vanilla-lazyload/dist/lazyload.iife.min.js"></script><div class="js-pjax"><script>function loadLivere () {
  if (typeof LivereTower === 'object') {
    window.LivereTower.init()
  }
  else {
    (function(d, s) {
        var j, e = d.getElementsByTagName(s)[0];
        if (typeof LivereTower === 'function') { return; }
        j = d.createElement(s);
        j.src = 'https://cdn-city.livere.com/js/embed.dist.js';
        j.async = true;
        e.parentNode.insertBefore(j, e);
    })(document, 'script');
  }
}

if ('Livere' === 'Livere' || !true) {
  if (true) btf.loadComment(document.getElementById('lv-container'), loadLivere)
  else loadLivere()
}
else {
  function loadOtherComment () {
    loadLivere()
  }
}</script></div><script defer="defer" id="fluttering_ribbon" mobile="false" src="https://cdn.jsdelivr.net/npm/butterfly-extsrc/dist/canvas-fluttering-ribbon.min.js"></script><script id="canvas_nest" defer="defer" color="0,0,255" opacity="0.7" zIndex="-1" count="99" mobile="false" src="https://cdn.jsdelivr.net/npm/butterfly-extsrc/dist/canvas-nest.min.js"></script><script id="click-heart" src="https://cdn.jsdelivr.net/npm/butterfly-extsrc/dist/click-heart.min.js" async="async" mobile="false"></script><script id="click-show-text" src="https://cdn.jsdelivr.net/npm/butterfly-extsrc/dist/click-show-text.min.js" data-mobile="false" data-text="富强,民主,文明,和谐,平等,公正,法治,爱国,敬业,诚信,友善" data-fontsize="15px" data-random="false" async="async"></script><script async data-pjax src="//busuanzi.ibruce.info/busuanzi/2.3/busuanzi.pure.mini.js"></script><div id="local-search"><div class="search-dialog"><nav class="search-nav"><span class="search-dialog-title">搜索</span><span id="loading-status"></span><button class="search-close-button"><i class="fas fa-times"></i></button></nav><div class="is-center" id="loading-database"><i class="fas fa-spinner fa-pulse"></i><span>  数据库加载中</span></div><div class="search-wrap"><div id="local-search-input"><div class="local-search-box"><input class="local-search-box--input" placeholder="搜索文章" type="text"/></div></div><hr/><div class="no-result" id="local-search-results"></div><div id="local-search-stats-wrap"></div></div></div><div id="search-mask"></div><script src="/js/search/local-search.js"></script></div></div><script src="/live2dw/lib/L2Dwidget.min.js?094cbace49a39548bed64abff5988b05"></script><script>L2Dwidget.init({"pluginRootPath":"live2dw/","pluginJsPath":"lib/","pluginModelPath":"assets/","tagMode":false,"debug":false,"model":{"jsonPath":"/live2dw/assets/haruto.model.json"},"display":{"position":"right","width":150,"height":300,"hOffset":-15,"vOffset":-15},"mobile":{"show":true},"react":{"opacity":0.7},"log":false});</script></body></html>